#include "PrecompiledHeader.h"

#include "utils.h"
#include "MTGDefinitions.h"
#include "Subtypes.h"
#include "WResourceManager.h"
#include "WFont.h"
#include <sys/stat.h>

#ifdef PSP
#include "pspsdk.h"
#endif

namespace wagic
{
#ifdef TRACK_FILE_USAGE_STATS
    std::map<std::string, int> ifstream::sFileMap;
#endif
}

using std::vector;
using std::queue;

int RandomGenerator::random()
{
    int result;
    if (!loadedRandomValues.size() || !log)
    {
        result = rand();
    }
    else
    {
        result = loadedRandomValues.front();
        loadedRandomValues.pop_front();
    }
    if(log)
        usedRandomValues.push_back(result);
    return result;
}

ostream& RandomGenerator::saveUsedRandValues(ostream& out) const
{
    list<int>::const_iterator ite;
    for(ite=usedRandomValues.begin(); ite != usedRandomValues.end(); ite++)
    {
        out << *ite << ",";
    }

    return out;
}

ostream& RandomGenerator::saveLoadedRandValues(ostream& out) const
{
    list<int>::const_iterator ite;
    for(ite=loadedRandomValues.begin(); ite != loadedRandomValues.end(); ite++)
    {
        out << *ite << ",";
    }

    return out;
}

void RandomGenerator::loadRandValues(string s)
{
    loadedRandomValues.clear();
    usedRandomValues.clear();

    while (s.size())
    {
        unsigned int value;
        size_t limiter = s.find(",");
        if (limiter != string::npos)
        {
            value = atoi(s.substr(0, limiter).c_str());
            s = s.substr(limiter + 1);
        }
        else
        {
            value = atoi(s.c_str());
            s = "";
        }
        if (value) loadedRandomValues.push_back(value);
    }
}

int WRand(bool)
{
    return rand();
}

bool fileExists(const char * filename)
{
    return JFileSystem::GetInstance()->FileExists(filename);
}

bool FileExists(const string & filename)
{
    return JFileSystem::GetInstance()->FileExists(filename);
}

std::string cReplaceString(string subject, const string& search, const string& replace)
{
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != string::npos)
    {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
    return subject;
}

/*
#ifdef LINUX

#include <execinfo.h>
void dumpStack()
{
void* buffer[50];
int s = backtrace(buffer, 50);
char** tab = backtrace_symbols(buffer, s);
for (int i = 1; i < s; ++i) printf("%s\n", tab[i]);
printf("\n");
free(tab);
}

#endif
*/

/* RAM simple check functions source */

// *** FUNCTIONS ***

u32 ramAvailableLineareMax(void)
{
    u32 size, sizeblock;
    u8 *ram;

    // Init variables
    size = 0;
    sizeblock = RAM_BLOCK;

#ifdef PSP
    int disableInterrupts = pspSdkDisableInterrupts();
#endif

    // Check loop
    while (sizeblock)
    {
        // Increment size
        size += sizeblock;

        // Allocate ram
        ram = (u8 *) malloc(size);

        // Check allocate
        if (!(ram))
        {
            // Restore old size
            size -= sizeblock;

            // Size block / 2
            sizeblock >>= 1;
        }
        else
            free(ram);
    }

#ifdef PSP
    pspSdkEnableInterrupts(disableInterrupts);
#endif

    return size;
}

u32 ramAvailable(void)
{
    u8 **ram, **temp;
    u32 size, count, x;

    // Init variables
    ram = NULL;
    size = 0;
    count = 0;

#ifdef PSP
    int disableInterrupts = pspSdkDisableInterrupts();
#endif

    // Check loop
    for (;;)
    {
        // Check size entries
        if (!(count % 10))
        {
            // Allocate more entries if needed
            temp = (u8**) realloc(ram, sizeof(u8 *) * (count + 10));
            if (!(temp)) break;

            // Update entries and size (size contains also size of entries)
            ram = temp;
            size += (sizeof(u8 *) * 10);
        }

        // Find max lineare size available
        x = ramAvailableLineareMax();
        if (!(x)) break;

        // Allocate ram
        ram[count] = (u8 *) malloc(x);
        if (!(ram[count])) break;

        // Update variables
        size += x;
        count++;
    }

    // Free ram
    if (ram)
    {
        for (x = 0; x < count; x++)
            free(ram[x]);
        free(ram);
    }

#ifdef PSP
    pspSdkEnableInterrupts(disableInterrupts);
#endif
    return size;
}

/* String manipulation functions */
string& trim(string& str)
{
    str = ltrim(str);
    str = rtrim(str);
    return str;
}

string& ltrim(string& str)
{
    str.erase(0, str.find_first_not_of(" \t"));
    return str;
}

string& rtrim(string& str)
{
    str.resize(str.find_last_not_of(" \t") + 1);
    return str;
}

std::vector<std::string>& split(const std::string& s, char delim, std::vector<std::string>& elems)
{
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim))
    {
        elems.push_back(item);
    }
    return elems;
}

std::string join(vector<string>& v, string delim)
{
    std::string retVal;
    for ( vector<string>::iterator it = v.begin(); it != v.end(); ++it )
    {
        retVal.append( *it );
        retVal.append( delim );
    }

    return retVal;
}

std::vector<std::string> split(const std::string& s, char delim)
{
    std::vector<std::string> elems;
    return split(s, delim, elems);
}

std::vector<std::string>&  parseBetween(const std::string& s, string start, string stop, bool stopRequired, std::vector<std::string>& elems)
{
    size_t found = s.find(start);
    if (found == string::npos)
        return elems;
    
    size_t offset = found + start.size();
    size_t end = s.find(stop, offset);
    if (end == string::npos && stopRequired)
        return elems;

    elems.push_back(s.substr(0,found));
    if (end != string::npos)
    {
        elems.push_back(s.substr(offset, end - offset));
        elems.push_back(s.substr(end + 1));
    }
    else
    {
        elems.push_back(s.substr(offset));
        elems.push_back("");
    }

    return elems;
}

std::vector<std::string> parseBetween(const std::string& s, string start, string stop, bool stopRequired)
{
    std::vector<std::string> elems;
    return parseBetween(s, start, stop, stopRequired, elems);
}

// This is a customized word wrap based on pixel width.  It tries it's best 
// to wrap strings using spaces as delimiters.  
// Not sure how this translates into non-english fonts.
std::string wordWrap(const std::string& sentence, float width, int fontId)
{
    WFont * mFont = WResourceManager::Instance()->GetWFont(fontId);
    float lineWidth = mFont->GetStringWidth( sentence.c_str() );
    string retVal = sentence;
    if ( lineWidth < width ) return sentence;

    int numLines = 1;
    int breakIdx = 0;
    for( size_t idx = 0; idx < sentence.length(); idx ++ )
    {
        if ( sentence[idx] == ' ' )
        {
            string currentSentence = sentence.substr(breakIdx, idx - breakIdx);
            float stringLength = mFont->GetStringWidth( currentSentence.c_str() );
            if (stringLength >= width)
            {                
                if ( stringLength > width )
                {
                    while ( sentence[idx-1] != ' ' )
                        idx--;
                }
                retVal[idx-1] = '\n';                
                breakIdx = idx;
                numLines++;
            }
        }
        else if ( sentence[idx] == '\n' )
        {
            string currentSentence = sentence.substr(breakIdx, idx - breakIdx);
            float stringLength = mFont->GetStringWidth( currentSentence.c_str() );
            if (stringLength >= width)
            {                
                if ( stringLength > width )
                {
                    while ( sentence[idx-1] != ' ' )
                        idx--;
                    retVal[idx-1] = '\n';                
                }
                numLines++;
            }
            breakIdx = idx;
            numLines++;
        }
    }

    return retVal;
}


unsigned long hash_djb2(const char *str)
{
    unsigned long hash = 5381;
    int c;

    while ((c = *str++))
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

std::string buildFilePath(const vector<string> & folders, const string & filename)
{
    string result = "";
    for (size_t i = 0; i < folders.size(); ++i)
    {
        result.append(folders[i]);
        if (result[result.length()-1] != '/')
        {
            result.append("/");
        }
    }

    result.append(filename);
    return result;

}
std::string ensureFolder(const std::string & folderName)
{
    if (!folderName.size())
        return "";

    string result = folderName;
    if (result[result.length()-1] != '/')
    {
        result.append("/");
    }
    return result;
}

void ReplaceString(std::string& subject, const std::string& search, const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

bool StartsWith(const std::string& base, const char *start)
{
    return base.compare(0, strlen(start), start) == 0;
}

bool StartsWith(const std::string& base, const std::string& start)
{
    return base.compare(0, start.length(), start) == 0;
}

