#include "ui.h"
#include "../exceptions/exceptions.h"
#include "../domain/trench.h"
#include "../validator/validator.h"
#include "../repository/Repository.h"
#include "../repository/MemoryRepository.h"
#include "../repository/CSVRepository.h"
#include "../repository/HTMLRepository.h"
#include "../controllers/admin_controller/admin_controller.h"
#include "../controllers/user_controller/user_controller.h"
#include <stdlib.h>
#include <vector>
#include <string>
#include <memory>
#include <iostream>

UI::UI(){
    std::shared_ptr<FileRepository> repo = std::make_shared<CSVRepository>("repo.csv",true);;
    std::shared_ptr<FileRepository> shoppingRepo;
    while(true){
        std::cout<<"1. CSV Repository"<<std::endl;
        std::cout<<"2. HTML Repository"<<std::endl;
        std::cout<<"Option: ";
        std::string buffer;
        getline(std::cin,buffer);
        if(buffer.length()!=1){
            std::cout<<"Invalid option!"<<std::endl;
            continue;
        }
        char option = buffer[0];
        switch(option){
            case '1':
                shoppingRepo = std::make_shared<CSVRepository>("shoppingRepo.csv");
                break;
            case '2':
                shoppingRepo = std::make_shared<HTMLRepository>("shoppingRepo.html");
                break;
            default:
                std::cout<<"Invalid option!"<<std::endl;
                continue;
        }
        break;
    }

    this->_adminController = std::make_unique<AdminController>(repo);
    this->_userController = std::make_unique<UserController>(repo,shoppingRepo);
}


UI::~UI(){

}

void UI::run(){
    char option = 0;
    std::string buffer;
    while(1){
        std::cout<<"1. Run as admin"<<std::endl;
        std::cout<<"2. Run as user"<<std::endl;
        std::cout<<"3. Exit"<<std::endl;
        std::cout<<"Option: ";
        std::getline(std::cin,buffer);
        if(buffer.length()!=1){
            std::cout<<"Invalid option!"<<std::endl;
            continue;
        }
        option = buffer[0];
        switch(option){
            case '1':
                this->runAsAdmin();
                break;
            case '2':
                this->runAsUser();
                break;
            case '3':
                return;
            default:
                std::cout<<"Invalid option!"<<std::endl;
                break;
        }
    }
}

void UI::runAsAdmin(){
    char option = 0;
    std::string buffer;
    while(1){
        std::cout<<"1. Add trench"<<std::endl;
        std::cout<<"2. Remove trench"<<std::endl;
        std::cout<<"3. Update trench"<<std::endl;
        std::cout<<"4. List all trenches"<<std::endl;
        std::cout<<"5. Exit"<<std::endl;
        std::cout<<"Option: ";
         std::getline(std::cin,buffer);
        if(buffer.length()!=1){
            std::cout<<"Invalid option!"<<std::endl;
            continue;
        }
        option = buffer[0];
        switch(option){
            case '1':
                {   
                    try{
                        std::string buffer;
                        int size;
                        std::string colour;
                        int price;
                        int quantity;
                        std::string photograph;
                        std::cout<<"Size: ";
                        getline(std::cin,buffer);
                        Validator::validate_size(buffer);
                        size = std::stoi(buffer);
                        std::cout<<"Colour: ";
                        getline(std::cin,colour);
                        Validator::validate_colour(colour);
                        std::cout<<"Price: ";
                        getline(std::cin,buffer);
                        Validator::validate_price(buffer);
                        price = std::stoi(buffer);
                        std::cout<<"Quantity: ";
                        getline(std::cin,buffer);
                        Validator::validate_quantity(buffer);
                        quantity = std::stoi(buffer);
                        std::cout<<"Photograph: ";
                        getline(std::cin,photograph);
                        Validator::validate_photograph(photograph);
                        this->_adminController->add(size,colour,price,quantity,photograph);
                        std::cout<<"Trench added!"<<std::endl;
                    }
                    catch(ValidatorException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                    catch(RepoException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                }
                break;
            case '2':
                {
                    try{
                        std::string buffer;
                        int size;
                        std::string colour;
                        std::string photograph;
                        std::cout<<"Size: ";
                        getline(std::cin,buffer);
                        Validator::validate_size(buffer);
                        size = std::stoi(buffer);
                        std::cout<<"Colour: ";
                        getline(std::cin,colour);
                        Validator::validate_colour(colour);
                        std::cout<<"Photograph: ";
                        getline(std::cin,photograph);
                        Validator::validate_photograph(photograph);
                        this->_adminController->remove(size,colour,photograph);
                        std::cout<<"Trench removed!"<<std::endl;
                    }
                    catch(ValidatorException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                    catch(RepoException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                }
                break;
            case '3':
                {
                    try{
                        std::string buffer;
                        int size;
                        std::string colour;
                        int price;
                        int quantity;
                        std::string photograph;
                        std::cout<<"Size: ";
                        getline(std::cin,buffer);
                        Validator::validate_size(buffer);
                        size = std::stoi(buffer);
                        std::cout<<"Colour: ";
                        getline(std::cin,colour);
                        Validator::validate_colour(colour);
                        std::cout<<"Price: (type enter to not change it) ";
                        getline(std::cin,buffer);
                        if (buffer.length()!=0){
                            Validator::validate_price(buffer);
                            price = std::stoi(buffer);
                        }
                        else 
                            price = -1;
                        std::cout<<"Quantity: (type enter to not change it) ";
                        getline(std::cin,buffer);
                        if (buffer.length()!=0){
                            Validator::validate_quantity(buffer);
                            quantity = std::stoi(buffer);
                        }
                        else
                            quantity = -1;
                        std::cout<<"Photograph: ";
                        getline(std::cin,photograph);
                        Validator::validate_photograph(photograph);
                        this->_adminController->update(size,colour,price,quantity,photograph);
                        std::cout<<"Trench updated!"<<std::endl;
                    }
                    catch(ValidatorException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                    catch(RepoException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                    catch(AdminControllerException& e){
                        std::cout<<e.what()<<std::endl;
                    }
                }
                break;
            case '4':
                for(Trench i:*this->_adminController->getAll()){
                    std::cout<<i;
                }
                break;
            case '5':
                return;
            default:
                std::cout<<"Invalid option!"<<std::endl;
                break;
        }
    }
}

void UI::runAsUser(){
    char option;
    std::string buffer;
    while(1){
        std::cout<<"1. Show all trench coats in a given size"<<std::endl;
        std::cout<<"2. Show shopping cart"<<std::endl;
        std::cout<<"3. Exit"<<std::endl;
        std::cout<<"Option: ";
        std::getline(std::cin,buffer);
        if(buffer.length()!=1){
            std::cout<<"Invalid option!"<<std::endl;
            continue;
        }
        option = buffer[0];
        switch(option){
            case '1':
            {
                std::string buffer;
                int size;
                std::cout<<"Size: ";
                getline(std::cin,buffer);
                if (buffer.length()!=0){
                    try{
                        Validator::validate_size(buffer);
                    }
                    catch(ValidatorException& e){
                        std::cout<<e.what()<<std::endl;
                        continue;
                    }
                    size = std::stoi(buffer);
                }
                else
                    size = -1;
                std::vector<Trench> iterator = this->_userController->getTrenchCarousel(size);
                if(iterator.size()==0){
                    std::cout<<"No trench coats found!"<<std::endl;
                    continue;
                }
                int exit = 1;
                int index = 0;
                while(exit && iterator.size()){
                    Trench s = iterator[index];
                    std::cout<< s << std::endl;
                    std::cout<<"Total price: "<<this->_userController->shoppingListTotal()<<std::endl;
                    char option;
                    std::string buffer;
                    std::cout<<"1. Add to shopping cart"<<std::endl;
                    std::cout<<"2. Next trench coat"<<std::endl;
                    std::cout<<"3. Exit"<<std::endl;
                    std::cout<<"Option: ";
                    std::getline(std::cin,buffer);
                    if(buffer.length()!=1){
                        std::cout<<"Invalid option!"<<std::endl;
                        continue;
                    }
                    option = buffer[0];
                    switch(option){
                        case '1':
                        {
                            this->_userController->addProductToShoppingList(iterator[index]);
                            iterator[index].quantity(iterator[index].quantity()-1);
                            if (iterator[index].quantity() == 0){
                                iterator.erase(iterator.begin()+index);
                                if(index==(int)(iterator.size()))
                                    index=0;
                            }
                            break;
                        }
                        case '2':
                        {
                            index++;
                            if(index==(int)(iterator.size()))
                                index=0;
                            break;
                        }
                        case '3':
                        {
                            exit=0;
                            break;
                        }
                        default:
                            std::cout<<"Invalid option!"<<std::endl;
                            break;

                    }
                    
                
                }
                break;
            }
            case '2':
            {
                this->_userController->openShoppingList();
            }
            
            case '3':
            {
                return;
            }
        }
    }
}

