/*
* psplog.cpp -- Christophe Painchaud aka Atanor, DaSH <dash@ionblast.net>
*
* Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
*
*
* 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 (version 2 of the License)
* 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 <psconfig.h>

#include <csutil/csstring.h>
#include <iutil/objreg.h>
#include <ivaria/reporter.h>
#include "util/consoleout.h"
#include "util/log.h"
#include <iutil/cfgmgr.h>


namespace pslog
{

iObjectRegistry* logger;
bool disp_flag[MAX_FLAGS];
uint32 filters_id[MAX_FLAGS];

char *flagnames[] = {   "LOG_ANY",
                        "LOG_WEATHER",
                        "LOG_SPAWN",
                        "LOG_CELPERSIST",
                        "LOG_PAWS",
                        "LOG_GROUP",
                        "LOG_CHEAT",
                        "LOG_LINMOVE",
                        "LOG_SPELLS",
                        "LOG_NEWCHAR",
                        "LOG_SUPERCLIENT",
                        "LOG_EXCHANGES",
                        "LOG_ADMIN",
                        "LOG_STARTUP",
                        "LOG_CHARACTER",
                        "LOG_CONNECTIONS",
                        "LOG_CHAT",
                        "LOG_NET",
                        "LOG_LOAD",
                        "LOG_NPC",
                        "LOG_TRADE",
                        "LOG_SOUND",
                        "LOG_COMBAT",
                        "LOG_SKILLXP",
                        "LOG_QUESTS",
                        "LOG_SCRIPT",
                        "LOG_MARRIAGE",
                        "LOG_MESSAGES",
                        "LOG_CACHE",
                        "LOG_PETS",
                        "LOG_USER",
                        "LOG_LOOT",
                        "LOG_DUELS"
}; // End of flagnames

char *flagsetting[] = { "Planeshift.Log.Any",
                        "Planeshift.Log.Weather",
                        "Planeshift.Log.Spawn",
                        "Planeshift.Log.Cel",
                        "Planeshift.Log.PAWS",
                        "Planeshift.Log.Group",
                        "Planeshift.Log.Cheat",
                        "Planeshift.Log.Linmove",
                        "Planeshift.Log.Spells",
                        "Planeshift.Log.Newchar",
                        "Planeshift.Log.Superclient",
                        "Planeshift.Log.Exchanges",
                        "Planeshift.Log.Admin",
                        "Planeshift.Log.Startup",
                        "Planeshift.Log.Character",
                        "Planeshift.Log.Connections",
                        "Planeshift.Log.Chat",
                        "Planeshift.Log.Net",
                        "Planeshift.Log.Load",
                        "Planeshift.Log.NPC",
                        "Planeshift.Log.Trade",
                        "Planeshift.Log.Sound",
                        "Planeshift.Log.Combat",
                        "Planeshift.Log.SkillXP",
                        "Planeshift.Log.Quests",
                        "Planeshift.Log.Script",
                        "Planeshift.Log.Marriage",
                        "Planeshift.Log.Messages",
                        "Planeshift.Log.Cache",
                        "Planeshift.Log.Pets",
                        "Planeshift.Log.User",
                        "Planeshift.Log.Loot",
                        "Planeshift.Log.Duels"
}; // End of flagsettings

bool DoLog(int severity, LOG_TYPES type, uint32 filter_id)
{
    if (logger == 0)
        return false;
    if (!disp_flag[type] && severity > CS_REPORTER_SEVERITY_WARNING)
        return false;
    if (filters_id[type]!=0 && filter_id!=0 && filters_id[type]!=filter_id)
        return false;
    return true;
}


void LogMessage (const char* file, int line, const char* function,
             int severity, LOG_TYPES type, uint32 filter_id, const char* msg, ...)
{
    if (!DoLog(severity,type,filter_id)) return;

    va_list arg;

    ConsoleOutMsgClass con = CON_SPAM;
    switch (severity)
    {
        case CS_REPORTER_SEVERITY_WARNING: con = CON_WARNING; break;
        case CS_REPORTER_SEVERITY_NOTIFY: con = CON_NOTIFY; break;
        case CS_REPORTER_SEVERITY_ERROR: con = CON_ERROR; break;
        case CS_REPORTER_SEVERITY_BUG: con = CON_BUG; break;
        case CS_REPORTER_SEVERITY_DEBUG: con = CON_DEBUG; break;
    }

    va_start(arg, msg);
    CPrintfLog (con, msg, arg);
    va_end(arg);

    if(con <= ConsoleOut::GetMaximumOutputClassStdout())
    {
        char msgid[5000];
        snprintf (msgid, 5000, "<%s:%d %s>",
            file, line, function);

        va_start(arg, msg);
        csReportV (logger, severity, msgid, msg, arg);
        va_end(arg);

        // Log errors to a file so they can be emailed to devs daily.
        if (severity == CS_REPORTER_SEVERITY_ERROR ||
            severity == CS_REPORTER_SEVERITY_BUG)
        {
            FILE *f = fopen("errorlog.txt","a");
            if (f)
            {
                fprintf(f,msgid);
                fprintf(f," ");
                va_start(arg, msg);
                vfprintf(f,msg,arg);
                va_end(arg);
                fclose(f);
            }
        }

    }
}


void Initialize(iObjectRegistry* object_reg)
{
    logger = object_reg;

    static bool initialized = false;
    if (initialized) return;
    initialized = true;

    for (int i = 0; i < MAX_FLAGS; i++)
    {
        disp_flag[i] = false;
    }

    disp_flag[LOG_ANY]         = true;
    disp_flag[LOG_CONNECTIONS] = true;
    disp_flag[LOG_CHAT]        = true;
    disp_flag[LOG_NET]         = true;
    disp_flag[LOG_CHARACTER]   = true;
    disp_flag[LOG_NEWCHAR]     = true;
}

void SetFlag(const char *name,bool flag, uint32 filter)
{
    for (int i=0; i<MAX_FLAGS; i++)
    {
        if (!flagnames[i])
            continue;

        if (!strcmp(flagnames[i],name) || !strcmp(name,"all"))
        {
            disp_flag[i] = flag;

            CPrintf(CON_CMDOUTPUT, "%s flag %s ",flagnames[i],flag?"activated":"deactivated");
            if (filter!=0 && filter!=-1) {
              filters_id[i]=filter;
              CPrintf(CON_CMDOUTPUT, "with filter %d \n",filter);
            } else {
              filters_id[i]=0;
              CPrintf(CON_CMDOUTPUT, "with no filter.\n");
            }

            if (strcmp(name,"all"))
                return;
        }

    }
    if (strcmp(name,"all"))
        CPrintf(CON_CMDOUTPUT, "No flag found with the name '%s'!\n",name);
}

void DisplayFlags(const char *name)
{
    for (int i=0; i<MAX_FLAGS; i++)
    {
        if (!name || !strcmp(flagnames[i],name))
        {
            CPrintf(CON_CMDOUTPUT, "%s = %s ",flagnames[i],disp_flag[i]?"true":"false");
            if (filters_id[i]!=0)
                CPrintf(CON_CMDOUTPUT, "with filter %d \n",filters_id[i]);
            else
              CPrintf(CON_CMDOUTPUT, "\n",filters_id[i]);
        }
    }
}

bool GetValue(const char *name)
{
    for (int i=0; i< MAX_FLAGS; i++)
    {
        if (flagnames[i] && !strcmp(flagnames[i],name))
        {
            return disp_flag[i];            
        }
    }
    return false;
}

char* GetName(int id)
{
    return flagnames[id];
}

char* GetSettingName(int id)
{
    return flagsetting[id];
}


} // end of namespace pslog

LogCSV::LogCSV(iConfigManager* configmanager, iVFS* vfs)
{
    csString filename[MAX_CSV];
    csString header;

    size_t maxSize = configmanager->GetInt("Planeshift.LogCSV.MaxSize", 10*1024*1024);
    filename[CSV_PALADIN] = configmanager->GetStr("Planeshift.LogCSV.File.Paladin");
    filename[CSV_EXCHANGES] = configmanager->GetStr("Planeshift.LogCSV.File.Exchanges");
    filename[CSV_AUTHENT] = configmanager->GetStr("Planeshift.LogCSV.File.Authent");
    filename[CSV_STATUS] = configmanager->GetStr("Planeshift.LogCSV.File.Status");
    filename[CSV_ADVICE] = configmanager->GetStr("Planeshift.LogCSV.File.Advice");
    filename[CSV_ECONOMY] = configmanager->GetStr("Planeshift.LogCSV.File.Economy");
    filename[CSV_STUCK] = configmanager->GetStr("Planeshift.LogCSV.File.Stuck");

    for(int i = 0;i < MAX_CSV;i++)
    {
        if(filename[i].IsEmpty())
        {
            csvFile[i] = NULL;
            continue;
        }
        if (!(vfs->Exists(filename[i])))
        {
            csvFile[i] = vfs->Open(filename[i],VFS_FILE_WRITE);
            switch(i)
            {
            case CSV_PALADIN: 
                header = "Date/Time, Client, Type, Sector, Start pos (xyz), Maximum displacement, Real displacement, Start velocity, Angular velocity, Paladin version\n";
                break;
            case CSV_EXCHANGES:
                header = "Date/Time, Source Client, Target Client, Type, Item, Quantity, Cost\n";
                break;
            case CSV_AUTHENT:
                header = "Date/Time, Client, Client ID, Details\n";
                break;
            case CSV_STATUS:
                header = "Date/Time, Status\n";
                break;
            case CSV_ADVICE:
                header = "Date/Time, Source Client, Target Client, Message\n";
                break;
            case CSV_ECONOMY:
                header = "Action, Count, Item, Quality, From, To, Price, Time\n";
                break;
            case CSV_STUCK:
                header = "Date/Time, Client, Race, Gender, Sector, PosX, PosY,"
                         " PosZ, Direction\n";
                break;
            }
            csvFile[i]->Write(header.GetData(), header.Length());
            csvFile[i]->Flush();
        }
        else
        {

            csvFile[i] = vfs->Open(filename[i],VFS_FILE_APPEND);
            if (csvFile[i] && csvFile[i]->GetSize() > maxSize)
            {
                CPrintf(CON_ERROR, "Log File %s is too big! Current size is: %u", filename[i].GetData(), csvFile[i]->GetSize());
                csvFile[i] = vfs->Open(filename[i],VFS_FILE_WRITE);
            }
        }
    }
}

void LogCSV::Write(int type, csString& text)
{
    if (!csvFile[type])
        return;


    time_t curtime = time(NULL);
    struct tm *loctime;
    loctime = localtime (&curtime);
    csString buf(asctime(loctime));
    buf.Truncate(buf.Length()-1);

    buf.Append(", ");
    buf.Append(text);
    buf.Append("\n");

    csvFile[type]->Write(buf, buf.Length());

    static unsigned count = 0;

    if (type == CSV_STATUS)
        csvFile[type]->Flush();
    else
    {
        // Unfair flushing mechanism for the time being.
        count++;
        if(count % 5 == 0)
        {
            count = 0;
            csvFile[type]->Flush();
        }
    }
}
