#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lists.h"
#include "tokens.h"

// The token data type needs to remember:
// - what type it is (left paren, right paren, operator, or integer).
// - if it is an operator, what operator it is
// - if it is an integer, what integer it is.
// For the type, we use the following convention:
//      token_type = 0 => LEFT_PAREN
//      token_type = 1 => RIGHT_PAREN
//      token_type = 2 => OPERATOR
//      token_type = 3 => INTEGER

const int LEFT_PAREN_TYPE = 0;
const int RIGHT_PAREN_TYPE = 1;
const int OPERATOR_TYPE = 2;
const int INTEGER_TYPE = 3;

struct token_struct
{
   int token_type;
   char operator;
   int integer;
};

Token newToken()
{
   Token result = malloc(sizeof(result));
   result->token_type = -1;
   result->operator = 0;
   result->integer = 0;
}

int tokenIsLeftParen(Token token)
{
   return (token->token_type == LEFT_PAREN_TYPE);
}

int tokenIsRightParen(Token token)
{
   return (token->token_type == RIGHT_PAREN_TYPE);
}

int tokenIsOperator(Token token)
{
   return (token->token_type == OPERATOR_TYPE);
}


int tokenIsInt(Token token)
{
   return (token->token_type == INTEGER_TYPE);
}

char tokenOperator(Token token)
{
   if (tokenIsOperator(token) == 0)
   {
      tokenPrint(token);
      printf("error: this token is not an operator\n");
      exit(1);
   }
   
   return token->operator;
}

int tokenInt(Token token)
{
   if (tokenIsInt(token) == 0)
   {
      tokenPrint(token);
      printf("error: this token is not an operator\n");
      exit(1);
   }
   
   return token->integer;
}


void tokenPrint(Token token)
{
   if (token->token_type == LEFT_PAREN_TYPE)
   {
      printf("(");
   }
   else if (token->token_type == RIGHT_PAREN_TYPE)
   {
      printf(")");
   }
   else if (token->token_type == OPERATOR_TYPE)
   {
      printf("%c", token->operator);
   }
   else if (token->token_type == INTEGER_TYPE)
   {
      printf("%d", token->integer);
   }
}

// some auxiliary functions, to interface tokens with lists and stacks.
void printTokenList(list tokens)
{
   link i;
   printf("\n");
   
   for (i = listFirst(tokens); i != NULL; i = linkNext(i))
   {
      Token token = (Token) linkItem(i);
      tokenPrint(token);
      printf(" ");
   }
   printf("\n");   
}


// This is the most important function, it converts a string into
// a list of tokens.
list tokenize(char * input)
{
   list result = newList();
   
   int index = 0;
   while (input[index] != 0)
   {
      Token token = newToken();
      char current = input[index];
      if (current == '(')
      {
         token->token_type = LEFT_PAREN_TYPE;
      }
      else if (current == ')')
      {
         token->token_type = RIGHT_PAREN_TYPE;
      }
      else if ((current == '+') || (current == '-') || 
              (current == '*') || (current == '/')) 
      {
         token->token_type = OPERATOR_TYPE;
         token->operator = current;
      }
      else if ((current >= '0') && (current <= '9'))
      {
         token->token_type = INTEGER_TYPE;
         int number = 0;
         while((current >= '0') && (current <= '9'))
         {
            number = 10 * number + (current - '0');
            index++;
            current = input[index];
         }
         index--;
         token->integer = number;
      }
      
      if (token->token_type != -1)
      {
         insertAtEnd(result, newLink(token));
      }
      index++;
   }
   
   return result;
}

