#include "controller.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>

Controller* _Controller_Constructor(Repository* repository,UndoController* undo_controller){
    Controller* controller = (Controller*)malloc(sizeof(Controller));
    if (controller==NULL)
        return NULL;
    controller->repository=repository;
    controller->undo_controller=undo_controller;
    return controller;
}

void _Controller_Destructor(Controller* controller){
    if (controller==NULL)
        return;
    _Repository_Destructor(controller->repository);
    _UndoController_Destructor(controller->undo_controller);
    controller->repository=NULL;
    free(controller);
}

int _Controller_add(Controller* controller, char *name, char *supplier, int quantity, int day, int month, int year){
    if (controller==NULL)
        return 1;
    if (name==NULL)
        return 1;
    if (supplier==NULL)
        return 1;
    Material* material = _Material_Constructor(name,supplier,quantity,day,month,year);
    int response = _Repository_add(controller->repository,material);
    switch (response)
    {
    case 0:
    {
        Vector* argsDo = _Vector_Constructor(2);
        _Vector_add(argsDo,controller->repository);
        _Vector_add(argsDo,material);
        FunctionCall* doFunction = _FunctionCall_Constructor(_Repository_add,argsDo);
        Vector* argsUndo = _Vector_Constructor(2);
        _Vector_add(argsUndo,controller->repository);
        char* copy_name=(char*)malloc(255);
        strcpy(copy_name,name);
        char* copy_supplier = (char*)malloc(255);
        strcpy(copy_supplier,supplier);
        Material* copy_material = _Material_Constructor(copy_name,copy_supplier,quantity,day,month,year);
        _Vector_add(argsUndo,copy_material);
        FunctionCall* undoFunction = _FunctionCall_Constructor(_Repository_delete,argsUndo);
        Operation* operation = _Operation_Constructor(doFunction,undoFunction);
        _UndoController_recordOperation(controller->undo_controller,operation);
        return 0;
    }
    case 1:
    {
        return 1;
    }
    case 2:
    {
        Vector* argsDo = _Vector_Constructor(2);
        _Vector_add(argsDo,controller->repository);
        _Vector_add(argsDo,material);
        FunctionCall* doFunction = _FunctionCall_Constructor(_Repository_add,argsDo);
        Vector* argsUndo = _Vector_Constructor(2);
        _Vector_add(argsUndo,controller->repository);
        char* copy_name=(char*)malloc(255);
        strcpy(copy_name,name);
        char* copy_supplier = (char*)malloc(255);
        strcpy(copy_supplier,supplier);
        Material* negativeMaterial = _Material_Constructor(copy_name,copy_supplier,-_Material_GetQuantity(material),_Material_GetDate(material).tm_mday,_Material_GetDate(material).tm_mon+1,_Material_GetDate(material).tm_year);
        _Vector_add(argsUndo,negativeMaterial);
        FunctionCall* undoFunction = _FunctionCall_Constructor(_Repository_add,argsUndo);
        Operation* operation = _Operation_Constructor(doFunction,undoFunction);
        _UndoController_recordOperation(controller->undo_controller,operation);
        return 2;
    }

    default:
        return 1;
    }

}

int _Controller_delete(Controller* controller, char *name, char *supplier, int day, int month, int year){
    if (controller==NULL)
        return 1;
    if (name==NULL)
        return 1;
    if (supplier==NULL)
        return 1;
    Material* material = _Material_Constructor(name,supplier,0,day,month,year);
    int response = _Repository_delete(controller->repository,material);
    switch (response)
    {
    case 0:
    {
        Vector* argsDo = _Vector_Constructor(2);
        _Vector_add(argsDo,controller->repository);
        _Vector_add(argsDo,material);
        FunctionCall* doFunction = _FunctionCall_Constructor(_Repository_delete,argsDo);
        Vector* argsUndo = _Vector_Constructor(2);
        _Vector_add(argsUndo,controller->repository);
        char* copy_name=(char*)malloc(255);
        strcpy(copy_name,name);
        char* copy_supplier = (char*)malloc(255);
        strcpy(copy_supplier,supplier);
        Material* copy_material = _Material_Constructor(copy_name,copy_supplier,_Material_GetQuantity(material),day,month,year);
        _Vector_add(argsUndo,copy_material);
        FunctionCall* undoFunction = _FunctionCall_Constructor(_Repository_add,argsUndo);
        Operation* operation = _Operation_Constructor(doFunction,undoFunction);
        _UndoController_recordOperation(controller->undo_controller,operation);
        return 0;
    }
    case 1:
    {
        return 1;
    }

    default:
        return 1;
    }
}

int _Controller_update(Controller* controller,char *name, char *supplier, int quantity, int day, int month, int year){
    if (controller==NULL)
        return 1;
    if (name==NULL)
        return 1;
    if (supplier==NULL)
        return 1;
    Material* material = _Material_Constructor(name,supplier,quantity,day,month,year);
    int response = _Repository_update(controller->repository,material);
    switch (response)
    {
    case 0:
    {
        Vector* argsUndo = _Vector_Constructor(2);
        _Vector_add(argsUndo,controller->repository);
        _Vector_add(argsUndo,material);
        FunctionCall* undoFunction = _FunctionCall_Constructor(_Repository_update,argsUndo);
        Vector* argsDo = _Vector_Constructor(2);
        _Vector_add(argsDo,controller->repository);
        char* copy_name=(char*)malloc(255);
        strcpy(copy_name,name);
        char* copy_supplier = (char*)malloc(255);
        strcpy(copy_supplier,supplier);
        Material* new_material = _Material_Constructor(copy_name,copy_supplier,quantity,day,month,year);
        _Vector_add(argsDo,new_material);
        FunctionCall* doFunction = _FunctionCall_Constructor(_Repository_update,argsDo);
        Operation* operation = _Operation_Constructor(doFunction,undoFunction);
        _UndoController_recordOperation(controller->undo_controller,operation);
        return 0;
    }
    case 1:
    {
        return 1;
    }

    default:
        return 1;
    }
}

int _Controller_getLenght(Controller* controller){
    if (controller==NULL)
        return 0;
    return _Repository_getLenght(controller->repository);
}

Material* _Controller_getItem(Controller* controller,int index){
    if (controller==NULL)
        return NULL;
    return _Repository_getItem(controller->repository,index);
}

Material** _Controller_getExpiredMaterial(Controller* controller,char* string,int* lenght){
    int len=_Controller_getLenght(controller);
    Material** vector = (Material**)malloc(sizeof(Material)*len);
    time_t t = time(NULL);
    struct tm current_time = *localtime(&t);
    current_time.tm_year+=1900;
    string[strlen(string)-1]='\0';
    for(int i=0;i<len;i++){
        Material* material=_Controller_getItem(controller,i);
        if ((strstr(material->name,string)!=NULL||strlen(string)==0||string[0]=='\n')&&(material->expiration_date.tm_year<current_time.tm_year ||(material->expiration_date.tm_year==current_time.tm_year && material->expiration_date.tm_mon<current_time.tm_mon)||(material->expiration_date.tm_year==current_time.tm_year && material->expiration_date.tm_mon==current_time.tm_mon && material->expiration_date.tm_mday<current_time.tm_mday))){
            vector[*lenght]=material;
            (*lenght)++;
        }
    }
    return vector;
}

Vector* _Controller_getShortSupplyItems(Controller* controller, int quantity){
    int len = _Controller_getLenght(controller);
    Vector* vector = _Vector_Constructor(5);
    for(int i=0;i<len;i++){
        Material* material=_Controller_getItem(controller,i);
        if(_Material_GetQuantity(material)<quantity)
            _Vector_add(vector,material);
    }
    for(int i=0;i<_Vector_getLength(vector)-1;i++){
        for(int j=i;j<_Vector_getLength(vector);j++){
            if(_Material_GetQuantity((Material*)_Vector_getItem(vector,i)) > _Material_GetQuantity((Material*)_Vector_getItem(vector,j))){
                TElem aux = vector->elems[i];
                vector->elems[i] = vector->elems[j];
                vector->elems[j] = aux;
            }
        }
    }

    return vector;
}

int _Controller_undo(Controller* controller){
    if (controller==NULL)
        return 1;
    return _UndoController_undo(controller->undo_controller);
}

int _Controller_redo(Controller* controller){
    if (controller==NULL)
        return 1;
    return _UndoController_redo(controller->undo_controller);
}

