package Language::Farnsworth::Value::Lambda;

use strict;
use warnings;

use Language::Farnsworth::Dimension;
use Language::Farnsworth::Value;
use Carp;
use Language::Farnsworth::Error;

use Data::Dumper;

use utf8;

our $VERSION = 0.6;

use overload 
    '+' => \&add,
    '-' => \&subtract,
    '*' => \&mult,
    '/' => \&div,
	'%' => \&mod,
	'**' => \&pow,
	'<=>' => \&compare,
;#	'bool' => \&bool;

use base qw(Language::Farnsworth::Value);

#this is the REQUIRED fields for Language::Farnsworth::Value subclasses
#
#dimen => a Language::Farnsworth::Dimension object
#
#this is so i can make a -> conforms in Language::Farnsworth::Value, to replace the existing code, i'm also planning on adding some definitions such as, TYPE_PARI, TYPE_STRING, TYPE_LAMBDA, TYPE_DATE, etc. to make certain things easier

sub new
{
  my $class = shift;
  my $scope = shift;
  my $args = shift;
  my $code = shift;
  my $branches = shift;
  my $outmagic = shift; #i'm still not sure on this one

  debug 5, "Need error checking in lambda creation!";

  my $self = {};

  bless $self, $class;

  $self->{outmagic} = $outmagic;

  $self->{scope} = $scope;
  $self->{code} = $code;
  $self->{args} = $args;
  $self->{branches} = $branches;
  
  return $self;
}

sub getcode
{
	return $_[0]->{code};
}

sub getargs
{
	return $_[0]->{args};
}

sub getscope
{
	return $_[0]->{scope};
}

sub getbranches
{
	return $_[0]->{branches};
}

#######
#The rest of this code can be GREATLY cleaned up by assuming that $one is of type, Language::Farnsworth::Value::Pari, this means that i can slowly redo a lot of this code

sub add
{
  my ($one, $two, $rev) = @_;

  confess "Non reference given to addition" unless ref($two);

  #if we're not being added to a Language::Farnsworth::Value::Pari, the higher class object needs to handle it.
  confess "Scalar value given to addition to Lambda" if ($two->isa("Language::Farnsworth::Value::Pari"));
  return $two->add($one, !$rev) unless ($two->ismediumtype());
  if (!$two->istype("Lambda"))
  {
    confess "Given non boolean to boolean operation";
  }


  #NOTE TO SELF this needs to be more helpful, i'll probably do this by creating an "error" class that'll be captured in ->evalbranch's recursion and use that to add information from the parse tree about WHERE the error occured
  die "Adding lambda is not a good idea\n"; 
}

sub subtract
{
  my ($one, $two, $rev) = @_;

  confess "Non reference given to subtraction" unless ref($two);

  #if there's a higher type, use it, subtraction otherwise doesn't make sense on arrays
  die "Scalar value given to subtraction to Lambda" if ($two->isa("Language::Farnsworth::Value::Pari"));
  return $two->subtract($one, !$rev) unless ($two->ismediumtype());
  if (!$two->istype("Lambda"))
  {
    confess "Given non boolean to lambda operation";
  }

  die "Subtracting lambdas? what did you think this would do, create a black hole?";
}

sub modulus
{
  my ($one, $two, $rev) = @_;

  confess "Non reference given to modulus" unless ref($two);

  #if there's a higher type, use it, subtraction otherwise doesn't make sense on arrays
  confess "Scalar value given to modulus to lambda" if ($two->isa("Language::Farnsworth::Value::Pari"));
  return $two->mod($one, !$rev) unless ($two->ismediumtype());
  if (!$two->istype("Lambda"))
  {
    confess "Given non lambda to lambda operation";
  }

  die "Modulusing lambda? what did you think this would do, create a black hole?";
}

sub mult
{
  my ($one, $two, $rev) = @_;

  confess "Non reference given to multiplication" unless ref($two);

  #if there's a higher type, use it, subtraction otherwise doesn't make sense on arrays
  #confess "Scalar value given to multiplcation to lambda. ED: This will make white holes later" if ($two->isa("Language::Farnsworth::Value::Pari"));
  return $two->mult($one, !$rev) unless ($two->ismediumtype() || $two->istype("Pari") || $two->istype("Lambda"));
  
#  if (!$two->istype("Lambda"))
#  {
#    confess "Given non lambda to lambda operation";
#  }

  my $args = $two->istype("Array") ? $two :  new Language::Farnsworth::Value::Array([$two]); 

  #this code is debug code, but i'm afraid to take it out, when i put it here it started working properly
  #print "LAMBDAMULT\n";
  #eval{print Dumper($one->{scope}->{vars}->getvar('x'), $one->{scope}->{vars}->getvar('y'))}; #this bug was fixed

  return $one->{scope}->{funcs}->calllambda($one, $args); #needs to be updated

#  die "Multiplying lambdas? what did you think this would do, create a black hole? ED: this will make black holes later";
}

sub div
{
  my ($one, $two, $rev) = @_;

  print "INSIDE LAMBDA DIVISION\n";

  confess "Non reference given to division" unless ref($two);

  #if there's a higher type, use it, subtraction otherwise doesn't make sense on arrays
  
  return $two->div($one, !$rev) unless ($two->ismediumtype()|| $two->istype("Pari") || $two->istype("Lambda"));

  if ($two->isa("Language::Farnsworth::Value::Pari"))
  {
	  #ok i've got a simple thing here i think!
	  #its not simple, will not be simple and will not end up working right, this is a hack to make 10 kg per cubic meter, and the like to work, until i add objects
	  my $onevalue = Language::Farnsworth::Value::Pari->new(1); #don't use 1.0 it'll coerce things into floats unneccesarily
	  my $newv = $onevalue * $one; #this'll remultiply things!
	  my $ret = $two / $newv; #this COULD be dangerous since i can see how to make an inf loop in a division this way
	  return $ret;
  }

  if (!$two->istype("Lambda"))
  {
    confess "Given non boolean to lambda operation";
  }

  die "Dividing lambdas? what did you think this would do, create a black hole?";
}

#sub bool
#{
	#my $self = shift;

	#seems good enough of an idea to me
	#i have a bug HERE
	#print "BOOLCONV\n";
	#print Dumper($self);
	#print "ENDBOOLCONV\n";
#	return 1; #for now lambdas are ALWAYS true!
#}

sub pow
{
  my ($one, $two, $rev) = @_;

  confess "Non reference given to exponentiation" unless ref($two);

  #if there's a higher type, use it, subtraction otherwise doesn't make sense on arrays
  confess "Exponentiating arrays? what did you think this would do, create a black hole?" if ($two->isa("Language::Farnsworth::Value::Pari"));
  return $two->pow($one, !$rev) unless ($two->ismediumtype());
  if (!$two->istype("Lambda"))
  {
    confess "Given non boolean to lambdas operation";
  }

  die "Exponentiating lambdas? what did you think this would do, create a black hole?";
}

sub compare
{
  my ($one, $two, $rev) = @_;

  confess "Non reference given to compare" unless ref($two);

  #if we're not being added to a Language::Farnsworth::Value::Pari, the higher class object needs to handle it.
  confess "Scalar value given to division to lambdas" if ($two->istype("Pari"));
  return $two->compare($one, !$rev) unless ($two->istype("Lambda"));

  return 0; #i don't have any metric for comparing lambdas, so... they'll always be equal
}

