/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#include "Log.h"
#include "Settings.h"
#include "StringUtil.h"
#include "FileUtil.h"


// This class is used by Log. So we cannot use LOG? macros here.
#define _LOGD_(x...) do { fprintf(stderr, x); fprintf(stderr, "\n"); } while(0)

// reported generated under reports/YYYY_MM_DD_HH_MM_SS dir
const char reportTopDir[] = "reports";
android::String8 FileUtil::mDirPath;

bool FileUtil::prepare(android::String8& dirPath)
{
    if (mDirPath.length() != 0) {
        dirPath = mDirPath;
        _LOGD_("mDirPath %s", mDirPath.string());
        return true;
    }

    time_t timeNow = time(NULL);
    if (timeNow == ((time_t)-1)) {
        _LOGD_("time error");
       return false;
    }
    // tm is allocated in static buffer, and should not be freed.
    struct tm* tm = localtime(&timeNow);
    if (tm == NULL) {
        _LOGD_("localtime error");
        return false;
    }
    int result = mkdir(reportTopDir, S_IRWXU);
    if ((result == -1) && (errno != EEXIST)) {
        _LOGD_("mkdir of topdir failed, error %d", errno);
        return false;
    }
    android::String8 reportTime;
    if (reportTime.appendFormat("%04d_%02d_%02d_%02d_%02d_%02d", tm->tm_year + 1900,
                tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec) != 0) {
            return false;
    }
    Settings::Instance()->addSetting(Settings::EREPORT_TIME, reportTime);
    android::String8 path;
    if (path.appendFormat("%s/%s", reportTopDir, reportTime.string()) != 0) {
        return false;
    }
    result = mkdir(path.string(), S_IRWXU);
    if ((result == -1) && (errno != EEXIST)) {
        _LOGD_("mkdir of report dir failed, error %d", errno);
        return false;
    }
    mDirPath = path;
    dirPath = path;

    return true;
}

FileUtil::FileUtil()
{
    mBuffer = new char[DEFAULT_BUFFER_SIZE];
    if (mBuffer == NULL) {
        // cannot use ASSERT here, just crash
        abort();
    }
    mBufferSize = DEFAULT_BUFFER_SIZE;
}

FileUtil::~FileUtil()
{
    if (mFile.is_open()) {
        mFile.close();
    }
    delete[] mBuffer;
}

bool FileUtil::init(const char* fileName)
{
    if (fileName == NULL) {
        return true;
    }

    mFile.open(fileName, std::ios::out | std::ios::trunc);
    if (!mFile.is_open()) {
            return false;
        }
    return true;
}

bool FileUtil::doVprintf(bool fileOnly, int logLevel, const char *fmt, va_list ap)
{
    // prevent messed up log in multi-thread env. Still multi-line logs can be messed up.
    android::Mutex::Autolock lock(mWriteLock);
    while (1) {
        int start = 0;
        if (logLevel != -1) {
            mBuffer[0] = '0' + logLevel;
            mBuffer[1] = '>';
            start = 2;
        }
        int size;
        size = vsnprintf(mBuffer + start, mBufferSize - start - 2, fmt, ap); // 2 for \n\0
        if (size < 0) {
            fprintf(stderr, "FileUtil::vprintf failed");
            return false;
        }
        if ((size + start + 2) > mBufferSize) {
            //default buffer does not fit, increase buffer size and retry
            delete[] mBuffer;
            mBuffer = new char[2 * size];
            if (mBuffer == NULL) {
                // cannot use ASSERT here, just crash
                abort();
            }
            mBufferSize = 2 * size;
            // re-try
            continue;
        }
        size += start;
        mBuffer[size] = '\n';
        size++;
        mBuffer[size] = 0;

        if (!fileOnly) {
            fprintf(stdout, "%s", mBuffer);
        }
        if (mFile.is_open()) {
            mFile<<mBuffer;
        }
        return true;
    }
}

bool FileUtil::doPrintf(const char* fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    bool result = doVprintf(false, -1, fmt, ap);
    va_end(ap);
    return result;
}