// check.cc -
//
// ##Copyright##
// 
// Copyright 2000-2016 Peter Robinson  (pjr@itee.uq.edu.au)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.00 
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ##Copyright##
//
// $Id: check.cc,v 1.14 2006/01/31 23:17:49 qp Exp $

#include "atom_table.h"
//#include "dereference.h"
#include "error_value.h"
#include "heap_qp.h"

/* This stupid thing is for VC++ - it has trouble
 * with parens around pointers: eg (char*)
 */
typedef char* charptr;

extern AtomTable *atoms;

ErrorValue
Heap::decode_stream(IOManager& iom,
		    Object* stream_cell,
		    QPStream **stream_handle,
		    const IODirection dir)
{
  assert(dir == QP_INPUT || dir == QP_OUTPUT);

  QPStream *strmptr = NULL;

  if (stream_cell->isInteger())
    {
      const int stream_number = stream_cell->getInteger();
      if (stream_number < 0 || stream_number >= (int)NUM_OPEN_STREAMS)
	{
	  return EV_TYPE;
	}
      strmptr = iom.GetStream(stream_number);
      if (strmptr == NULL)
	{
	  return EV_TYPE;
	}
    }
  else if (stream_cell->isAtom())
    {
          
      if (stream_cell == AtomTable::stream_stdin ||
	  stream_cell == AtomTable::stream_user ||
	  stream_cell == AtomTable::stream_user_input)
	{
	  if (dir == QP_INPUT)
	    {
	      strmptr = iom.StdIn();
	    }
	  else
	    {
	      return EV_NOT_PERMITTED;
	    }
	}
      else if (stream_cell == AtomTable::stream_stdout ||
	       stream_cell == AtomTable::stream_user ||
	       stream_cell == AtomTable::stream_user_output)
	{
	  if (dir == QP_OUTPUT)
	    {
	      strmptr = iom.StdOut();
	    }
	  else
	    {
	      return EV_NOT_PERMITTED;
	    }
	}
      else if (stream_cell == AtomTable::stream_stderr ||
	       stream_cell == AtomTable::stream_user_error)
	{
	  if (dir == QP_OUTPUT)
	    {
	     strmptr = iom.StdErr();
	    }
	  else
	    {
	      return EV_NOT_PERMITTED;
	    }
	}
      else
	{
	  return EV_VALUE;
	}
    }
  else if (stream_cell->isVariable())
    {
      return EV_INST;
    }
  else
    {
      return EV_TYPE;
    }
  
  if (strmptr == NULL)
    {
      return EV_VALUE;
    }
  else if (strmptr->Type() == QPSOCKET)
    {
      return EV_TYPE;
    }

  *stream_handle = strmptr;
  
  return EV_NO_ERROR;
}

ErrorValue
Heap::decode_stream_output(IOManager& iom,
			   Object* stream_cell,
			   QPStream **stream_handle)
{
  const ErrorValue ev = decode_stream(iom, stream_cell, stream_handle, QP_OUTPUT);
  if (ev == EV_NO_ERROR)
    {
      if ((*stream_handle)->getDirection() != QP_INPUT)
	{
	  return EV_NO_ERROR;
	}
      else 
	{
	  return EV_NOT_PERMITTED;
	}
    }
  else
    {
      return ev;
    }
}

ErrorValue
Heap::decode_stream_input(IOManager& iom,
			  Object* stream_cell,
			  QPStream **stream_handle)
{
  const ErrorValue ev = decode_stream(iom, stream_cell, stream_handle, QP_INPUT);
  if (ev == EV_NO_ERROR)
    {
      if ((*stream_handle)->getDirection() != QP_OUTPUT)
	{
	  return EV_NO_ERROR;
	}
      else 
	{
	  return EV_NOT_PERMITTED;
	}
    }
  else
    {
      return ev;
    }
}

ErrorValue
Heap::decode_nonneg_int(Object* integer_cell, int& integer)
{
  if (integer_cell->isVariable())
    {
      return EV_INST;
    }
  else if (integer_cell->isInteger())
    {
      integer = integer_cell->getInteger();
      if (integer < 0)
	{
	  return EV_VALUE;
	}
      else
	{
	  return EV_NO_ERROR;
	}
    }
  else
    {
      return EV_TYPE;
    }
}

bool
Heap::check_functor(Object* structure,
		    Atom* name, const word32 arity)
{
  if (structure->isStructure() &&
      arity == OBJECT_CAST(Structure*, structure)->getArity())
    {
      return name == OBJECT_CAST(Structure*, structure)->getFunctor();
    }
  else
    {
      return false;
    }
}

bool
Heap::decode_send_options(Object* options,
			  bool& remember_names,
			  bool& encode)
{
  if (options->isVariable())
    {
      return false;
    }
  remember_names = true;
  encode = true;
  if (options == AtomTable::nil)
    {
      return true;
    }
  while(options->isCons())
    {
      Cons* l = OBJECT_CAST(Cons*, options);
      Object* head = l->getHead()->variableDereference();
      options = l->getTail()->variableDereference();
      if (!head->isStructure())
	{
	  return false;
	}
      Structure* st = OBJECT_CAST(Structure*, head);
      Object* func = st->getFunctor()->variableDereference();
      if (func == atoms->add("remember_names"))
	{
	  remember_names = (st->getArgument(1)->variableDereference() 
			    == AtomTable::success);
	}
      else if (func == atoms->add("encode"))
	{
	  encode = (st->getArgument(1)->variableDereference() 
			    == AtomTable::success);
	}
      else
	{
	  return false;
	}
    }

  if (options == AtomTable::nil)
    {
      return true;
    }
  else
    {
      return false;
    }
}

bool
Heap::decode_recv_options(Object* options,
			  Object*& remember_names,
			  Object*& peek,
			  Object*& encode)
{
  if (check_functor(options, AtomTable::tcp_recv_options, 3))
    {
      Structure* str = OBJECT_CAST(Structure*, options);
      
      remember_names = str->getArgument(1)->variableDereference();
      peek = str->getArgument(2)->variableDereference();
      encode = str->getArgument(3)->variableDereference();

      return true;
    }
  else
    {
      return false;
    }
}

ErrorValue
Heap::decode_recv_options(Object* options_object,
			  bool& remember_names,
			  bool& peek,
			  bool& encode)
{
  Object* remember_names_object;
  Object* peek_object;
  Object* encode_object;

  if (options_object->isVariable())
    {
      return EV_INST;
    }
  else if (decode_recv_options(options_object,
			       remember_names_object,
			       peek_object,
			       encode_object))
    {
      if (remember_names_object->isVariable())
	{
	  return EV_INST;
	}
      else if (remember_names_object->isAtom())
	{
	  remember_names = atoms->atomToBool(remember_names_object);
	}
      else
	{
	  return EV_TYPE;
	}

      if (peek_object->isVariable())
	{
	  return EV_INST;
	}
      else if (peek_object->isAtom())
	{
	  peek = atoms->atomToBool(peek_object);
	}
      else
	{
	  return EV_TYPE;
	}

      if (encode_object->isVariable())
	{
	  return EV_INST;
	}
      else if (encode_object->isAtom())
	{
	  encode = atoms->atomToBool(peek_object);
	  return EV_NO_ERROR;
	}
      else
	{
	  return EV_TYPE;
	}
    }
  else
    {
      return EV_TYPE;
    }
}


bool
Heap::check_atom_list(Object* atom_list_object,
		      size_t& length)
{
  length = 0;

  atom_list_object = atom_list_object->variableDereference();
  for (;atom_list_object->isNil() || atom_list_object->isCons();)
    {
      if (atom_list_object->isNil())
	{
	  return true;
	}
      else if (atom_list_object->isCons())
	{
	  if (! OBJECT_CAST(Cons*, atom_list_object)->getHead()->variableDereference()->isAtom())
	    {
	      return false;
	    }
	  length++;
	  atom_list_object = OBJECT_CAST(Cons*, atom_list_object)->getTail()->variableDereference();
	}
    }

  return false;
}






