#ifndef __STATEMENT_H__
#define __STATEMENT_H__

#include <string>
#include <iostream>
using namespace std;

#include "Program.h"
#include "StringExpression.h"
#include "BooleanExpression.h"

extern Program program;


/**
 *   Bazna klasa za sve naredbe
 */
class Statement {
 public:
    virtual ~Statement() {}    

    /**
     *   Metod koji vrsi izvrsavanje, odnosno interpretaciju naredbe
     */
    virtual void execute() = 0;

};


/**
 *   Klasa koja predstavlja naredbu za ispis (print)
 */
class PrintStatement : public Statement {
 public:
    PrintStatement(StringExpression* text) 
	: _text(text) {
    }

    ~PrintStatement() {
	delete _text;
    }

    virtual void execute() {
	cout << _text->value() << endl;
    }

 private:
    /**
     *   Izraz cija se vrednost ispisuje
     */
    StringExpression* _text;
};

/**
 *   Klasa koja predstavlja naredbu unosa (input)
 */
class InputStatement : public Statement {
 public:
    InputStatement(StringExpression* text, string var) 
	: _text(text), _var(var) {
    }

    ~InputStatement() {
	delete _text;
    }

    virtual void execute() {
	// Ispisujemo text
	cout << _text->value() << endl;
	
	// Citamo liniju sa ulaza
	string tmp;
	getline(cin, tmp);

	// Postavljamo vrednost promenljive na unetu nisku
	program.setStringVar(_var, tmp);
    }

 private:
    /**
     *   String koji se ispisuje pre unosa
     */
    StringExpression* _text;

    /**
     *   Ime promenljive koja se ucitava
     */
    string _var;
};


/**
 *   Klasa koja predstavlja naredbu bezuslovnog skoka (goto)
 */
class GotoStatement : public Statement {
 public:
    GotoStatement(int lineNumber)
	: _lineNumber(lineNumber) {
    }

    virtual void execute() {
	program.Goto(_lineNumber);
    }
    
 private:
    /**
     *   Broj promenljive na koju se skace
     */
    int _lineNumber;
};


/**
 *   Klasa koja predstavlja naredbu uslovnog grananja (if-then)
 */
class IfStatement : public Statement {
 public:
    IfStatement(BooleanExpression* condition, Statement* statement) 
	: _condition(condition), _statement(statement) {
    }

    virtual void execute() {
	if (_condition->value()) _statement->execute();
    }

 private:
    /**
     *   Uslov
     */
    BooleanExpression* _condition;

    /**
     *   Telo
     */
    Statement* _statement;
};

/**
 *   Klasa koja predstavlja naredbu dodele (let)
 */
class LetStatement : public Statement {
 public:
    LetStatement(string var, StringExpression* expression)
	: _var(var), _expression(expression) {
    }

    virtual void execute() {
	program.setStringVar(_var, _expression->value());
    }

 private:
    /**
     *   Ime promenljive
     */
    string _var;
    
    /**
     *   Izraz na koji se postavlja promenljiva
     */
    StringExpression* _expression;
};

/**
 *  Klasa koja predstavlja iterativnu naredbu (for)
 */
class ForStatement : public Statement {
 public:
    ForStatement(string var, int from, int to) 
	: _var(var), _from(from), _to(to) {
	
    }
    
    /**
     *  Vraca ime promenljive iteracije
     */
    string getVar() {
	return _var;
    }

    /**
     *  Vraca gornju granicu iteracije
     */
    int getTo() {
	return _to;
    }

    virtual void execute() {
	program.setIntVar(_var, _from);
    }

 private:
    /**
     *   Ime promenljive iteracije
     */
    string _var;

    /**
     *   Granice iteracije
     */
    int _from, _to;
};

/**
 *  Klasa koja predstavlja naredbu prelaska na sledeci korak iteracije (next)
 */
class NextStatement : public Statement {
 public:
    NextStatement(string var) 
	: _var(var) {
    }

    virtual void execute() {
	// Pronalazimo odgovarajucu for naredbu
	int forStatementNumber = program.getForStatementNumber(_var);
	if (forStatementNumber == -1) 
	    throw "Next without for";
	ForStatement* forStatement =  dynamic_cast<ForStatement*>(
	    program.getStatement(forStatementNumber));

	// Uvecavamo vrednost promenljive iteracije
	int newVal = program.getIntVar(_var) + 1;
	program.setIntVar(_var, newVal);

	// Ukoliko nismo stigli do kraja iteracije
	if (newVal <= forStatement->getTo()) {
	    // Skacemo na prvu naredbu posle for-a
	    program.Goto(forStatementNumber);
	    program.nextStatement();
	}
    }

 private:
    /**
     *  Ime promenljive iteracije
     */
    string _var;
};

#endif
