/***************************************************************************
 *   Copyright (C) 2006 by Jens Koehler                                    *
 *   irimi_at_gmx_de                                                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <cstdio>
#include <string>
#include <iostream>
#include <fstream>
#include <cctype>

#include "commands.h"
#include "coregateway.h"
#include "debugTracer.h"



namespace // static components
{
const int MAX_LOG_RETRIES = 5;

};

using namespace std;

CoreGateway::CoreGateway(const char *user,
                         const char *pw ) : p_user (user),
        p_pw (pw),
        b_login(false)
{}

CoreGateway::~CoreGateway()
{}



/** just a very simple solution */
bool CoreGateway::handleLogin (const std::string &userpasswd)
{
    string check (p_user);
    check.append(",");
    check.append(p_pw);
    b_login = (check == userpasswd);
#ifdef VDRAMGW_DEBUG

    if (!b_login)
    {
        tracer.debugOut (check.c_str());
        tracer.debugOut (userpasswd.c_str());
    }
#endif

    return b_login;
}




void CoreGateway::runGateway(int port, bool refreshCache)
{

    bool bReconnect = true;
    try
    {
        TCPSocketWrapper sockserver;

        sockserver.listen(port);

        tracer.infoOut("gateway is ready");

        // accept connection from client
        while (bReconnect)
        {
            tracer.infoOut("gateway is waiting for new connection ...");

            TCPSocketWrapper sock(sockserver.accept());

            tracer.infoOut("accepted connection from:");
            tracer.infoOut(sock.address());

            TCPStream stream(sock);

            bReconnect=transreceiveData(stream);
        }
    }
    catch (const SocketRunTimeException &e)
    {
        tracer.errorOut ("socket exception:");
        tracer.errorOut (e.what());

    }

    tracer.infoOut("gateway is closing socket connection");


}

bool CoreGateway::transreceiveData(TCPStream &stream) //throws SockeRunTimeException()
{
    int int_cmd = CMD_ERROR;
    string cmd_parameter;
    int retries=0;
    vector<string> result;
    bool ok=false;

    while (retries < MAX_LOG_RETRIES )
    {
        stream >> int_cmd ; // wait for the command
        //cout << "new cmd:" << int_cmd << endl;
        if (!b_login && int_cmd != CMD_REQUEST_LOGIN && int_cmd != CMD_CHECK_VERSION )
        {
            tracer.infoOut("AUTHENTIFICATION REQUIRED");
            retries++;
            continue;
        }

        switch (int_cmd)
        {
        case CMD_CHECK_VERSION:
            getParameter (stream, cmd_parameter);
            if (cmd_parameter.find(VDRAMGW_VERSION))
            {
                stream << CMD_ERROR  << endl;
                string msg="WRONG CLIENT VERSION:";
                msg.append(cmd_parameter);
                msg.append("expected is ");
                msg.append("GATEWAY_VERSION");
                tracer.errorOut(msg.c_str());
                retries = MAX_LOG_RETRIES+1;
            }
            else
                stream << int_cmd  << endl;

            break;
        case CMD_REQUEST_LOGIN: // login
            getParameter (stream, cmd_parameter);
            handleLogin (cmd_parameter) ? stream << CMD_REQUEST_LOGIN << endl : stream << CMD_ERROR << endl;
            break;
        case CMD_REQUEST_PLAYLIST:
        case CMD_REQUEST_ARTISTLIST:
        case CMD_REQUEST_GENRELIST:
        case CMD_REQUEST_ALBUMLIST:
        case CMD_REQUEST_STATISTICS:
        case CMD_REQUEST_LIST:
            getParameter (stream, cmd_parameter);
            ok=listRequest (stream, int_cmd, cmd_parameter, result);
            sendAnswer(stream, int_cmd,result,ok);
            break;
        case CMD_REQUEST_ADDARTIST:
        case CMD_REQUEST_ADDGENRE:
        case CMD_REQUEST_ADDALBUM:
        case CMD_REQUEST_ADDTITLE:
            addRequest (stream,int_cmd);
            break;
        /*case CMD_REQUEST_PLAYLIST:
            getParameter (stream,cmd_parameter);
            if (requestData(cmd_parameter,result) && result.size()>=1 )
            {
                if (readPlaylistFile(result))
                {
                    stream << int_cmd  << endl; // ACK
                    std::vector<std::string>::iterator p=result.begin();
                    while (p != result.end())
                    {
                        stream << *p << endl;
                        p++;
                    }
                    break;
                }
            }
            else
                stream << CMD_ERROR  << endl;

            break;*/
        case CMD_REQUEST_GET:
            getParameter (stream,cmd_parameter);
            ok=requestData(cmd_parameter,result);
            sendAnswer(stream, int_cmd,result,ok);
            break;
        case CMD_REQUEST_SET:
            getParameter (stream,cmd_parameter);
            ok=requestData(cmd_parameter,result);
            sendAnswer (stream, int_cmd, ok);
            break;
        case CMD_REQUEST_CLIENTDOWN: // client shutdown
            tracer.infoOut("CMD_REQUEST_CLIENTDOWN");
            retries = MAX_LOG_RETRIES+1;
            break;
        case CMD_REQUEST_SERVERDOWN: // server shutdown
            tracer.infoOut("CMD_REQUEST_SERVERDOWN");
            retries = MAX_LOG_RETRIES+1;
            return false;
        default:
            tracer.errorOut("unkown amarok_cmd type");
            stream << CMD_ERROR  << endl; // FAILED
            break;

        }
        result = vector<string> ();
        cmd_parameter.clear();
    }
    return true;
}

void CoreGateway::getParameter (TCPStream &stream,string &cmd_parameter)
{
    do
    {
        getline(stream, cmd_parameter);
    }
    while (cmd_parameter.empty());
}

void CoreGateway::getParameter (TCPStream &stream,vector<string>&parameterList)
{
    string cmd_parameter;
    do
    {
        getline(stream, cmd_parameter);
        if (!cmd_parameter.empty())
        {
            parameterList.push_back(cmd_parameter);
        }
    }
    while (cmd_parameter.empty());

}

void CoreGateway::sendAnswer (TCPStream &stream, const int int_cmd, vector<string>&result, bool ok)
{

    if (ok)
    {

        stream << int_cmd  << endl; // ACK
        vector<string>::iterator p=result.begin();
        while (p != result.end())
        {
            stream << *p << endl;
            p++;
        }
    }
    else
        stream << CMD_ERROR  << endl; // FAILED
}

void CoreGateway::sendAnswer (TCPStream &stream, const int int_cmd, bool ok)
{

  ok ? stream << int_cmd  << endl : stream << CMD_ERROR  << endl;

}

bool CoreGateway::listRequest (TCPStream &stream,const int int_cmd, const string &cmd_parameter, vector<string>&result)
{
    bool ok= false;

    result.push_back("<list>");

    switch (int_cmd)
    {
    case CMD_REQUEST_STATISTICS:
        //ok=queryCurrentTrackStatistics(result);
        break;
    case CMD_REQUEST_PLAYLIST:
        ok=buildPlayResultlist(cmd_parameter,result);
        break;
    case CMD_REQUEST_ARTISTLIST:
        ok=buildArtistsResultlist(result);
        break;
    case CMD_REQUEST_ALBUMLIST:
        ok=buildAlbumsResultlist(result);
        break;
    case CMD_REQUEST_GENRELIST:
        ok=buildGenresResultlist(result);
        break;
    case CMD_REQUEST_LIST:
        ok=requestData(cmd_parameter,result);
        break;
    default:
        tracer.infoOut("CMD_REQUEST_LIST_UNKNOWN");
        break;
    }

    result.push_back("</list>");
    return ok;
}

void CoreGateway::addRequest (TCPStream &stream,int int_cmd)
{
    vector<string> cmdList;
    getParameter(stream,cmdList);
    stream << int_cmd  << endl; // ALWAYS ACK

    switch (int_cmd)
    {
    case CMD_REQUEST_ADDARTIST:
        //tracer.debugOut("CMD_REQUEST_ADDARTIST");
        addArtists(cmdList);
        break;
    case CMD_REQUEST_ADDGENRE:
        //tracer.debugOut("CMD_REQUEST_ADDGENRE");
        addGenres(cmdList);
        break;
    case CMD_REQUEST_ADDALBUM:
        //tracer.debugOut("CMD_REQUEST_ADDALBUM");
        addAlbums(cmdList);
        break;
    case CMD_REQUEST_ADDTITLE:
        //tracer.debugOut("CMD_REQUEST_ADDTITLE");
        addTitles(cmdList);
        break;
    default:
        tracer.infoOut("CMD_REQUEST_ADD_UNKNOWN");
        break;
    }

}



bool CoreGateway::updateCache ()
{
    bool ok=buildArtistsResultlistCache();
    if(ok)
        ok=buildAlbumsResultlistCache();
    if(ok)
        ok=buildGenresResultlistCache();

    return ok;
}


bool CoreGateway::loadCache (const char* file,std::vector<std::string> &result)
{

    string strLine;

    ifstream in(file);
    if (!in)
    {
        return false;
    }
    else
    {
        while(in && getline(in,strLine) && !in.eof())
        {
            result.push_back(strLine);
        }
        in.close();
    }

    return true;

}

bool CoreGateway::saveCache (const char* file,std::vector<std::string> &result)
{
    string strLine;

    ofstream out(file);
    if (!out)
    {
        tracer.errorOut("Write SQL cache file error");
        return false;
    }
    else
    {

        vector<string>::iterator p=result.begin();
        while (p != result.end())
        {
            out << *p << endl;
            p++;
        }
        out.close();

    }

    return true;
}
