package Logos::Generator::MobileSubstrate::Method;
use strict;
use parent qw(Logos::Generator::Base::Method);

sub _originalMethodPointerDeclaration {
	my $self = shift;
	my $method = shift;
	if(!$method->isNew) {
		my $build = "static ";
		my $classargtype = $self->selfTypeForMethod($method);
		my $name = "(*".$self->originalFunctionName($method).")(".$classargtype.", SEL";
		my $argtypelist = join(", ", @{$method->argtypes});
		$name .= ", ".$argtypelist if $argtypelist;

		$name .= ")";
		$build .= Logos::Method::declarationForTypeWithName($self->returnTypeForMethod($method), $name);
		$build .= $self->functionAttributesForMethod($method);
		return $build;
	}
	return undef;
}

sub _methodPrototype {
	my $self = shift;
	my $method = shift;
	my $includeArgNames = 0 || shift;
	my $build = "static ";
	my $classargtype = $self->selfTypeForMethod($method);
	my $arglist = "";
	if($includeArgNames == 1) {
		map $arglist .= ", ".Logos::Method::declarationForTypeWithName($method->argtypes->[$_], $method->argnames->[$_]), (0..$method->numArgs - 1);
	} else {
		my $typelist = join(", ", @{$method->argtypes});
		$arglist = ", ".$typelist if $typelist;
	}

	my $name = $self->newFunctionName($method)."(".$classargtype.($includeArgNames?" self":"").", SEL".($includeArgNames?" _cmd":"").$arglist.")";
	$build .= Logos::Method::declarationForTypeWithName($self->returnTypeForMethod($method), $name);
	$build .= $self->functionAttributesForMethod($method);
	return $build;
}

sub definition {
	my $self = shift;
	my $method = shift;
	my $build = "";
	$build .= $self->_methodPrototype($method, 1);
	return $build;
}

sub originalCall {
	my $self = shift;
	my $method = shift;
	my $customargs = shift;
	return "" if $method->isNew;

	my $build = $self->originalFunctionName($method)."(self, _cmd";
	if(defined $customargs && $customargs ne "") {
		$build .= ", ".$customargs;
	} elsif($method->numArgs > 0) {
		$build .= ", ".join(", ",@{$method->argnames});
	}
	$build .= ")";
	return $build;
}

sub declarations {
	my $self = shift;
	my $method = shift;
	my $build = "";
	my $orig = $self->_originalMethodPointerDeclaration($method);
	$build .= $orig."; " if $orig;
	$build .= $self->_methodPrototype($method)."; ";
	return $build;
}

sub initializers {
	my $self = shift;
	my $method = shift;
	my $cgen = Logos::Generator::for($method->class);
	my $classvar = ($method->scope eq "+" ? $cgen->metaVariable : $cgen->variable);
	if(!$method->isNew) {
		my $r = "";
		$r .= "if (".$classvar.") {";
		$r .=   "if (class_getInstanceMethod(".$classvar.", ".$self->selectorRef($method->selector).")) {";
		$r .=     "MSHookMessageEx(".$classvar.", ".$self->selectorRef($method->selector).", (IMP)&".$self->newFunctionName($method).", (IMP*)&".$self->originalFunctionName($method).");";
		$r .=   "} else {";
		$r .=     "HBLogError(@\"logos: message not found [%s %s]\", \"".$method->class->name."\", \"".$method->selector."\");";
		$r .=   "}";
		$r .= "} else {";
		$r .=   "HBLogError(@\"logos: nil class %s\", \"".$method->class->name."\");";
		$r .= "}";
	} else {
		my $r = "";
		$r .= "{ ";
		if(!$method->type) {
			$r .= "char _typeEncoding[1024]; unsigned int i = 0; ";
			for ($method->return, "id", "SEL", @{$method->argtypes}) {
				my $typeEncoding = Logos::Method::typeEncodingForArgType($_);
				if(defined $typeEncoding) {
					my @typeEncodingBits = split(//, $typeEncoding);
					my $i = 0;
					for my $char (@typeEncodingBits) {
						$r .= "_typeEncoding[i".($i > 0 ? " + $i" : "")."] = '$char'; ";
						$i++;
					}
					$r .= "i += ".(scalar @typeEncodingBits)."; ";
				} else {
					$r .= "memcpy(_typeEncoding + i, \@encode($_), strlen(\@encode($_))); i += strlen(\@encode($_)); ";
				}
			}
			$r .= "_typeEncoding[i] = '\\0'; ";
		} else {
			$r .= "const char *_typeEncoding = \"".$method->type."\"; ";
		}
		$r .= "class_addMethod(".$classvar.", ".$self->selectorRef($method->selector).", (IMP)&".$self->newFunctionName($method).", _typeEncoding); ";
		$r .= "}";
		return $r;
	}
}

1;
