#![allow(non_camel_case_types)]

use std;
use lex;
use lex::Token;
use lex::Token_Type;

const MAX_VARS: u32 = 200;
// Exponent is the only binary op with a higher priority than unary ops.
const UNARY_RIGHT_PRIORITY: u32 = 12;
// Must be higher priority than any operator, so that exponent doesn't try
// to consume negative in "2^-3".
const UNARY_LEFT_PRIORITY: u32 = 20;
const BINARY_OP_ERROR_PRIORITY: u32 = 30;

#[derive(PartialEq)]
pub enum Binary_Op {
  None,
  Add, Subtract,
  Multiply, Modulus,
  Exponent, // right associative
  Divide, Integer_Divide,
  Bitwise_And, Bitwise_Or, Bitwise_Xor,
  Shift_Left, Shift_Right,
  Concat, // right associative
  Equal, Lesser, Lesser_Equal,
  Not_Equal, Greater, Greater_Equal,
  And, Or, Xor
}
fn left_priority(op: &Binary_Op) -> u32 {
  match *op {
    Binary_Op::None => 0,
    Binary_Op::Add => 10, Binary_Op::Subtract => 10,
    Binary_Op::Multiply => 11, Binary_Op::Modulus => 11,
    Binary_Op::Exponent => 14,
    Binary_Op::Divide => 11, Binary_Op::Integer_Divide => 11,
    Binary_Op::Bitwise_And => 6,
    Binary_Op::Bitwise_Or => 4,
    Binary_Op::Bitwise_Xor => 5,
    Binary_Op::Shift_Left => 7, Binary_Op::Shift_Right => 7,
    Binary_Op::Concat => 9,
    Binary_Op::Equal => 3, Binary_Op::Lesser => 3, Binary_Op::Lesser_Equal => 3,
    Binary_Op::Not_Equal => 3, Binary_Op::Greater => 3, Binary_Op::Greater_Equal => 3,
    Binary_Op::And => 2, Binary_Op::Or => 1, Binary_Op::Xor => 1,
  }
}
fn right_priority(op: &Binary_Op) -> u32 {
  match *op {
    Binary_Op::None => 0,
    Binary_Op::Add => 10, Binary_Op::Subtract => 10,
    Binary_Op::Multiply => 11, Binary_Op::Modulus => 11,
    Binary_Op::Exponent => 13,
    Binary_Op::Divide => 11, Binary_Op::Integer_Divide => 11,
    Binary_Op::Bitwise_And => 6,
    Binary_Op::Bitwise_Or => 4,
    Binary_Op::Bitwise_Xor => 5,
    Binary_Op::Shift_Left => 7, Binary_Op::Shift_Right => 7,
    Binary_Op::Concat => 8,
    Binary_Op::Equal => 3, Binary_Op::Lesser => 3, Binary_Op::Lesser_Equal => 3,
    Binary_Op::Not_Equal => 3, Binary_Op::Greater => 3, Binary_Op::Greater_Equal => 3,
    Binary_Op::And => 2, Binary_Op::Or => 1, Binary_Op::Xor => 1,
  }
}
#[derive(PartialEq)]
pub enum Unary_Op {
  None, Not, Bitwise_Not, Length, Negative,
}

pub struct Name_Expression {
  parts : Vec<Expression>,
  pass_self : bool,
}

pub enum Expression {
  None,
  Tok(Token),
  Binop(Binary_Op, Box<Expression>, Box<Expression>),
  Unop(Unary_Op, Box<Expression>),
  Name(Name_Expression),
  Error(Token, String),
}

pub enum Expr_Part {
  Value(Expression),
  Binop(Binary_Op),
  Unop(Unary_Op),
}

fn expr_part_to_value(part: Expr_Part) -> Expression {
  match part {
    Expr_Part::Value(val) => val,
    _ => Expression::None,
  }
}
fn expr_part_to_binop(part: Expr_Part) -> Binary_Op {
  match part {
    Expr_Part::Binop(op) => op,
    _ => Binary_Op::None,
  }
} 
fn expr_part_to_unop(part: Expr_Part) -> Unary_Op {
  match part {
    Expr_Part::Unop(op) => op,
    _ => Unary_Op::None,
  }
}

struct Parse_State<'a> {
  input: &'a Vec<Token>,
  strider: std::slice::Iter<'a, Token>,
  curr: std::option::Option<&'a Token>,
  allow_parse_errors: bool,
}
impl<'a> Parse_State<'a> {
  fn next(&mut self) {
    self.curr = self.strider.next();
    while self.curr != None {
      match self.curr.unwrap().kind {
        Token_Type::None | Token_Type::Error(_) |
        Token_Type::Space(_) | Token_Type::New_Line(_) |
        Token_Type::Comment(_) | Token_Type::Long_Comment(_,_) => {
          self.curr = self.strider.next();
        },
        _ => return,
      }
    }
  }
  fn parse_name_member(&mut self, &mut ret_name: Name_Expression) {
    let current = self.curr.clone();
    match current {
      None => {
        ret_name.push(Expression::Error(Token::new_blank(), "expected_name".to_string()));
      },
      Some(tork) => {
        let tok = tork.clone();
        match tok.kind {
          Token_Type::Name(ref val) => {
            ret_name.push(Expression::Tok(tok));
            self.next();
            match self.curr {
              None => {},
              Some(ntok) => {
                match ntok.kind {
                  Token_Type::Index_Open => {
                  },
                  Token_Type::Method_Call => {
                  },
                  Token_Type::Member => {
                  },
                  _ => {},
                };
              },
            };
          },
          _ => {
            ret_name.push(Expression::Error(tok, "expected_name".to_string()));
          },
        };
      },
    };
  }
  fn parse_name_index(&mut self, &mut ret_name: Name_Expression) {
  }
  fn tokens_to_name_expr(&mut self) -> Expression {
    let mut ret = Name_Expression{parts: Vec::new(), pass_self: false};
    loop {
      {
        let current = self.curr.clone();
        match current {
          None => {
            ret.push(Expression::Error(Token::new_blank(), "name_unexpected_end"));
            return ret
          },
          Some(tork) => {
            let tok = tork.clone();
            match tok.kind {
              Token_Type::Name(_) => {
                ret.push(Expression::Tok(tok));
              },
              _ => {
                ret.push(Expression::Error(tok, "name_unexpected_token"));
                break;
              },
            };
          },
        };
      }
      self.next();
      let current = self.curr.clone();
      match current {
        None => {
          break;
        },
        Some(tork) => {
          let tok = tork.clone();
          match tok.kind {
            Token_Type::Member => {
              // foo.bar
            },
            Token_Type::Method_Call => {
              // foo:bar
            },
            Token_Type::Index_Open => {
              // foo[bar]
            },
            Token_Type::Paren_Open => {
              // foo(bar)
            },
            _ => {
              break;
            },
          };
        },
      };
      self.next();
    }
    ret
  }
  fn tokens_to_expr_parts(&mut self) -> Vec<Expr_Part> {
    let mut ret: Vec<Expr_Part> = Vec::new();
    let mut expect_value = true;
    let mut prev_was_value = false;
    let mut prev_lid = 0;
    let mut prev_cid = 0;
    let mut stuck_counter = 0;
    loop {
      let mut advance = true;
      let mut found_end = false;
      let mut is_value = true;
      let mut is_unop = false;
      let mut curr_lid = 0;
      let mut curr_cid = 0;
      let current = self.curr.clone();
      let part = match current {
        None => {
          found_end = true;
          Expr_Part::Value(Expression::None)
        },
        Some(tork) => {
          let tok = tork.clone();
          curr_lid = tok.line_id;
          curr_cid = tok.char_id;
          match tok.kind {
            Token_Type::Nil | Token_Type::False | Token_Type::True |
            Token_Type::Int(_) | Token_Type::Float(_) |
            Token_Type::Str(_,_)  | Token_Type::Long_Str(_,_) |
            Token_Type::Ellipsis => {
              Expr_Part::Value(Expression::Tok(tok))
            },
            Token_Type::Function => {
              // function definition, function must have no name
              Expr_Part::Value(Expression::Error(tok, "NIY".to_string()))
            },
            Token_Type::Name(_) => {
              self.tokens_to_name_expr()
            },
            Token_Type::Index_Close => {
              Expr_Part::Value(Expression::Error(tok, "unmatched_close_index".to_string()))
            },
            Token_Type::Paren_Open => {
              // prefix expression
              Expr_Part::Value(Expression::Error(tok, "NIY".to_string()))
            },
            Token_Type::Paren_Close => {
              Expr_Part::Value(Expression::Error(tok, "unmatched_close_paren".to_string()))
            },
            Token_Type::Table_Open => {
              // table constructor
              Expr_Part::Value(Expression::Error(tok, "NIY".to_string()))
            },
            Token_Type::Table_Close => {
              Expr_Part::Value(Expression::Error(tok, "unmatched_close_table".to_string()))
            },
            Token_Type::Bitwise_Not_Or_Xor | Token_Type::Subtract => {
              if expect_value {
                is_unop = true;
                Expr_Part::Unop(match tok.kind {
                  Token_Type::Bitwise_Not_Or_Xor => Unary_Op::Bitwise_Not,
                  Token_Type::Subtract => Unary_Op::Negative,
                  _ => Unary_Op::None,
                })
              } else {
                is_value = false;
                Expr_Part::Binop(match tok.kind {
                  Token_Type::Bitwise_Not_Or_Xor => Binary_Op::Bitwise_Xor,
                  Token_Type::Subtract => Binary_Op::Subtract,
                  _ => Binary_Op::None,
                })
              }
            },
            Token_Type::Not | Token_Type::Length => {
              is_unop = true;
              Expr_Part::Unop(match tok.kind {
                Token_Type::Not => Unary_Op::Not,
                Token_Type::Length => Unary_Op::Length,
                _ => Unary_Op::None, // unreachable
              })
            },
            Token_Type::And |
            Token_Type::Or |
            Token_Type::Xor |
            Token_Type::Concat |
            Token_Type::Equal |
            Token_Type::Greater |
            Token_Type::Greater_Equal |
            Token_Type::Lesser |
            Token_Type::Lesser_Equal |
            Token_Type::Not_Equal |
            Token_Type::Add |
            Token_Type::Divide |
            Token_Type::Integer_Divide |
            Token_Type::Multiply |
            Token_Type::Exponent |
            Token_Type::Modulus |
            Token_Type::Shift_Left |
            Token_Type::Shift_Right |
            Token_Type::Bitwise_And |
            Token_Type::Bitwise_Or => {
              is_value = false;
              Expr_Part::Binop(match tok.kind {
                Token_Type::And => Binary_Op::And,
                Token_Type::Or => Binary_Op::Or,
                Token_Type::Xor => Binary_Op::Xor,
                Token_Type::Concat => Binary_Op::Concat,
                Token_Type::Equal => Binary_Op::Equal,
                Token_Type::Greater => Binary_Op::Greater,
                Token_Type::Greater_Equal => Binary_Op::Greater_Equal,
                Token_Type::Lesser => Binary_Op::Lesser,
                Token_Type::Lesser_Equal => Binary_Op::Lesser_Equal,
                Token_Type::Not_Equal => Binary_Op::Not_Equal,
                Token_Type::Add => Binary_Op::Add,
                Token_Type::Divide => Binary_Op::Divide,
                Token_Type::Integer_Divide => Binary_Op::Integer_Divide,
                Token_Type::Multiply => Binary_Op::Multiply,
                Token_Type::Exponent => Binary_Op::Exponent,
                Token_Type::Modulus => Binary_Op::Modulus,
                Token_Type::Shift_Left => Binary_Op::Shift_Left,
                Token_Type::Shift_Right => Binary_Op::Shift_Right,
                Token_Type::Bitwise_And => Binary_Op::Bitwise_And,
                Token_Type::Bitwise_Or => Binary_Op::Bitwise_Or,
                _ => Binary_Op::None,
              })
            },
            Token_Type::Do | Token_Type::End | Token_Type::Return |
            Token_Type::Break | Token_Type::Goto | Token_Type::If |
            Token_Type::Then | Token_Type::Else | Token_Type::Else_If |
            Token_Type::For | Token_Type::While | Token_Type::Repeat |
            Token_Type::Until |
            Token_Type::Label_Marker | Token_Type::Local |
            Token_Type::Element_Separator | Token_Type::Assign => {
              found_end= true;
              Expr_Part::Value(Expression::None)
            },
            Token_Type::None |
            Token_Type::Comment(_) | Token_Type::Long_Comment(_,_) |
            Token_Type::Space(_) | Token_Type::New_Line(_) => {
              Expr_Part::Value(Expression::Error(tok, "parser bug: tokens_to_expr_parts should not see tokens that Parse_State::next is supposed to skip over.".to_string()))
            },
            Token_Type::Index_Open | Token_Type::Method_Call |
            Token_Type::Member => {
              Expr_Part::Value(Expression::Error(tok, "parser bug: token should have been handled by name parser".to_string()))
            },
            Token_Type::Error(_) => {
              Expr_Part::Value(Expression::Error(tok, "error_token".to_string()))
            },
          }
        }
      };
      if curr_lid == prev_lid && curr_cid == prev_cid {
        stuck_counter = stuck_counter + 1;
        if stuck_counter > 5 {
          println!("tokens_to_expr_parts stuck on {}, {}", curr_lid, curr_cid);
          found_end = true;
        }
      } else {
        stuck_counter = 0;
      }
      prev_lid = curr_lid;
      prev_cid = curr_cid;
      if found_end {
        break;
      } else {
        ret.push(part);
        if is_value {
          if is_unop {
            expect_value = true;
          } else {
            expect_value = false;
          }
          if prev_was_value {
            break;
          }
        } else {
          expect_value = true;
        }
        if is_unop {
        } else {
          prev_was_value = is_value;
        }
        if advance {self.next();}
      }
    }
    ret
  }
  
}
fn left_neighbor_priority(parts: &Vec<Expr_Part>, start: usize) -> u32 {
  match parts[start] {
    Expr_Part::Value(_) => {
      if start > 0 {
        let neighbor = start - 1;
        match parts[neighbor] {
          Expr_Part::Value(_) => 0,
          Expr_Part::Binop(ref op) => right_priority(&op),
          Expr_Part::Unop(_) => UNARY_RIGHT_PRIORITY,
        }
      } else {0}
    },
    Expr_Part::Binop(_) => BINARY_OP_ERROR_PRIORITY,
    Expr_Part::Unop(_) => UNARY_RIGHT_PRIORITY,
  }
}
fn right_neighbor_priority(parts: &Vec<Expr_Part>, start: usize) -> u32 {
  match parts[start] {
    Expr_Part::Value(_) => {
      if start < parts.len()-1 {
        let neighbor = start + 1;
        match parts[neighbor] {
          Expr_Part::Value(_) => 0,
          Expr_Part::Binop(ref op) => left_priority(&op),
          Expr_Part::Unop(_) => UNARY_LEFT_PRIORITY,
        }
      } else {0}
    },
    Expr_Part::Binop(_) => BINARY_OP_ERROR_PRIORITY,
    Expr_Part::Unop(_) => UNARY_LEFT_PRIORITY,
  }
}
pub fn binop_name(op: &Binary_Op) -> String {
  match *op {
    Binary_Op::None => "none".to_string(),
    Binary_Op::Add => "add".to_string(),
    Binary_Op::Subtract => "subtract".to_string(),
    Binary_Op::Multiply => "multiply".to_string(),
    Binary_Op::Modulus => "modulus".to_string(),
    Binary_Op::Exponent => "exponent".to_string(),
    Binary_Op::Divide => "divide".to_string(),
    Binary_Op::Integer_Divide => "intdivide".to_string(),
    Binary_Op::Bitwise_And => "bit_and".to_string(),
    Binary_Op::Bitwise_Or => "bit_or".to_string(),
    Binary_Op::Bitwise_Xor => "bit_xor".to_string(),
    Binary_Op::Shift_Left => "shift_left".to_string(),
    Binary_Op::Shift_Right => "shift_right".to_string(),
    Binary_Op::Concat => "concat".to_string(),
    Binary_Op::Equal => "equal".to_string(),
    Binary_Op::Lesser => "lesser".to_string(),
    Binary_Op::Lesser_Equal => "lesser_equal".to_string(),
    Binary_Op::Not_Equal => "not_equal".to_string(),
    Binary_Op::Greater => "greater".to_string(),
    Binary_Op::Greater_Equal => "greater_equal".to_string(),
    Binary_Op::And => "and".to_string(),
    Binary_Op::Or => "or".to_string(),
    Binary_Op::Xor => "xor".to_string(),
  }
}
pub fn unop_name(op: &Unary_Op) -> String {
  match *op {
    Unary_Op::None => "none".to_string(),
    Unary_Op::Not => "not".to_string(),
    Unary_Op::Bitwise_Not => "bit_not".to_string(),
    Unary_Op::Length => "length".to_string(),
    Unary_Op::Negative => "negative".to_string(),
  }
}
fn dump_expr(expr: &Expression, indent: usize) {
  match *expr {
    Expression::None => println!("{}none", "  ".repeat(indent)),
    Expression::Tok(ref tok) => println!("{}token", "  ".repeat(indent)),
    Expression::Binop(ref op, ref left, ref right) => {
      println!("{}binop {}", "  ".repeat(indent), binop_name(op));
      dump_expr(left, indent+1);
      dump_expr(right, indent+1);
    },
    Expression::Unop(ref op, ref right) => {
      println!("{}unop {}", "  ".repeat(indent), unop_name(op));
      dump_expr(right, indent+1);
    },
    Expression::Error(ref tok, ref why) => {
      println!("{}error: {}", "  ".repeat(indent), why);
    },
  };
}
fn dump_expr_parts(parts: &Vec<Expr_Part>) {
  for part in parts.iter() {
    match *part {
      Expr_Part::Value(ref sub) => {
        println!("value");
        dump_expr(sub, 1);
      },
      Expr_Part::Binop(ref op) => {
        println!("binop {}", binop_name(op));
      },
      Expr_Part::Unop(ref op) => {
        println!("unop {}", unop_name(op));
      },
    };
  }
}
pub fn parse_expression(input: Vec<Token>, dump_every_cycle: bool) -> Expression {
  let mut state = Parse_State{
    input: &input, strider: input.iter(), curr: None,
    allow_parse_errors: false,
  };
  // Advance to first useful token.
  state.next();
  // Read values and operators in until the end of the expression is found.
  // Walk the parts building a new list.  Any operator with greater
  // precedence than the adjacent operators attaches to its adjacent values
  // and becomes a value in the new list.
  // Repeat until there is only one entry in the list.
  let mut parts = state.tokens_to_expr_parts();
  let mut prev_len = parts.len();
  let mut stuck_counter = 0;
  let mut cycle_counter = 0;
  while parts.len() > 1 {
    let mut cid = 0;
    if dump_every_cycle {
      println!("cycle {}", cycle_counter);
      dump_expr_parts(&parts);
    }
    cycle_counter = cycle_counter + 1;
    while cid < parts.len() {
      enum Consume {
        None, Unary, Binary,
      };
      let mut eat = Consume::None;
      let mut left_error = false;
      let mut right_error = false;
      match parts[cid] {
        Expr_Part::Value(_) => {},
        Expr_Part::Binop(ref curr_op) => {
          if cid > 0 {
            let left_pri = left_neighbor_priority(&parts, cid-1);
            if cid < parts.len()-1 {
              let right_pri = right_neighbor_priority(&parts, cid+1);
              if
                if left_pri == BINARY_OP_ERROR_PRIORITY {
                  left_error = true; true
                } else {left_pri <= left_priority(curr_op)}
              &&
                if right_pri == BINARY_OP_ERROR_PRIORITY {
                  right_error = true; true
                } else {right_priority(curr_op) >= right_pri}
              {
                eat = Consume::Binary;
              }
            } else {
              right_error = true;
              if
                if left_pri == BINARY_OP_ERROR_PRIORITY {
                  left_error = true; true
                } else {left_pri <= left_priority(curr_op)}
              {
                eat = Consume::Binary;
              }
            }
          } else {
            left_error = true;
            if cid < parts.len()-1 {
              let right_pri = right_neighbor_priority(&parts, cid+1);
              if right_priority(curr_op) >= right_pri {
                eat = Consume::Binary;
              }
            } else {
              right_error = true;
              eat = Consume::Binary;
            }
          }
        },
        Expr_Part::Unop(_) => {
          if cid < parts.len()-1 {
            let right_pri = right_neighbor_priority(&parts, cid+1);
            if if right_pri == BINARY_OP_ERROR_PRIORITY {
              right_error = true; true
            } else {UNARY_RIGHT_PRIORITY > right_pri}
            {
              eat = Consume::Unary;
            }
          } else {
            right_error = true;
            eat = Consume::Unary;
          }
        },
      };
      match eat {
        Consume::None => {
          cid = cid + 1;
        },
        Consume::Unary => {
          let op;
          let rval;
          {
            let eat_last = if right_error {cid+1} else {cid+2};
            let mut consumed_parts = parts.drain(cid..eat_last);
            op = match consumed_parts.next() {
              None => Unary_Op::None,
              Some(part) => expr_part_to_unop(part),
            };
            rval = if right_error {
              Expression::Error(Token::new_blank(), "oper_requires_right_value".to_string())
            } else {
              match consumed_parts.next() {
                None => Expression::None,
                Some(part) => expr_part_to_value(part),
              }};
          }
          let expr = Expression::Unop(
            op, Box::new(rval));
          println!("inserting unary op at {}", cid);
          dump_expr(&expr, 1);
          let new_part = Expr_Part::Value(expr);
          parts.insert(cid, new_part);
          cid = cid + 1;
        },
        Consume::Binary => {
          let lval;
          let op;
          let rval;
          let insert_at;
          {
            let eat_first = if left_error {cid} else {cid-1};
            let eat_last = if right_error {cid+1} else {cid+2};
            let mut consumed_parts = parts.drain(eat_first..eat_last);
            lval = if left_error {
              Expression::Error(Token::new_blank(), "oper_requires_left_value".to_string())
            } else {
              match consumed_parts.next() {
                None => Expression::None,
                Some(part) => expr_part_to_value(part),
              }};
            op = match consumed_parts.next() {
              None => Binary_Op::None,
              Some(part) => expr_part_to_binop(part),
            };
            rval = if right_error {
              Expression::Error(Token::new_blank(), "oper_requires_right_value".to_string())
            } else {
              match consumed_parts.next() {
                None => Expression::None,
                Some(part) => expr_part_to_value(part),
              }};
            insert_at = eat_first;
          }
          let expr = Expression::Binop(
            op, Box::new(lval), Box::new(rval));
          println!("inserting binary op at {}", insert_at);
          dump_expr(&expr, 1);
          let new_part = Expr_Part::Value(expr);
          parts.insert(insert_at, new_part);
        },
      };
    }
    let curr_len = parts.len();
    if curr_len == prev_len {
      stuck_counter = stuck_counter + 1;
      if stuck_counter > 1 {
        println!("parse_expression stuck");
        dump_expr_parts(&parts);
        return Expression::Error(Token::new_blank(), "parser bug: parse_expression got stuck.".to_string())
      }
    } else {
      stuck_counter = 0;
    }
    prev_len = curr_len;
  }
  let ret = parts.pop().unwrap();
  match ret {
    Expr_Part::Value(val) => val,
    Expr_Part::Binop(_) | Expr_Part::Unop(_) => Expression::Error(Token::new_blank(), "parser bug: expression parts condensed to an operator instead of a value.".to_string()),
  }
}
