#include "data/Data.h"
#include "util/simpleobject/AgLong.h"
#include "context/Contexts.h"
#include "context/Context.h"
#include "server/UtilitiesContextConstants.h"
#include "datatable/DataTable.h"
#include "datatable/DataRecord.h"

Data::Data() : id(0)
{
}

Data::Data(const std::vector<unsigned char> &data) : id(0)
{
	this->data = data;
}

Data::Data(const AgString &name, const std::vector<unsigned char> &data) : id(0)
{
	this->data = data;
    this->name = name;
}

void Data::setPreview(const std::vector<unsigned char> &preview)
{
	this->preview = preview;
}

void Data::setId(int64_t id)
{
	this->id = id;
}

void Data::setData(const std::vector<unsigned char> &data)
{
	this->data = data;
}

void Data::setBlob(const std::vector<unsigned char> &blob)
{
	setData(blob);
}

void Data::setName(const AgString &name)
{
	this->name = name;
}

std::vector<unsigned char> Data::getPreview()
{
	return this->preview;
}

AgString Data::getName()
{
	return this->name;
}

int64_t Data::getId()
{
	return this->id;
}

std::vector<unsigned char> &Data::getData()
{
	return this->data;
}

std::vector<unsigned char> &Data::getBlob()
{
	return this->data;
}

std::map<AgString, AgObjectPtr> Data::getAttachments()
{
	return this->attachments;
}

int Data::checksum(const std::vector<unsigned char> &bytes)
{
	int sum = 0;
    for (std::vector<unsigned char>::const_iterator it = bytes.begin(); it!= bytes.end(); ++it) {
		sum += *it;
    }
	
    return sum;
}

AgString Data::formStringHelper(const AgString &str)
{
    if (str.length() > 0)
        return str;
    else
        return "null";
}

std::vector<unsigned char> Data::fetchData(ContextManager* cm, CallerControllerPtr cc)
{
    if (!data.empty())
    {
		return getData();
    }
    
    if (getId() == 0)
    {
        return std::vector<unsigned char>();
    }
    
    if (cm == NULL)
    {
        return std::vector<unsigned char>();
    }

    std::list<AgObjectPtr> pars;
    pars.push_back(AgObjectPtr(new AgLong(getId())));
    DataTablePtr dt = cm->get(Contexts::CTX_UTILITIES, cc)->callFunction(UtilitiesContextConstants::F_GET_DATA, cc, pars);

    data = dt->rec()->getData(UtilitiesContextConstants::FOF_GET_DATA_DATA)->getData();

    return data;
}

AgString Data::toDetailedString()
{
    AgString ss;

    ss += "Data [id: " + (id != 0 ? AgString::fromInt64(id) : "null") + ", name: " + formStringHelper(name) + ", preview: "
          + (preview.size() != 0 ? "len=" + AgString::fromInt(preview.size()) + " checksum=" + AgString::fromInt(checksum(preview)) : "null") + ", data: " + (data.size() != 0 ? "len=" + AgString::fromInt(data.size())
          + " checksum=" + AgString::fromInt(checksum(data)) : "null") + "]";

    return ss;
}

AgString Data::toString()
{
    AgString ss;
    ss += "Data [id: " + (id != 0 ? AgString::fromInt64(id) : "null") + ", name: " + formStringHelper(name) + ", preview: " +
            (preview.size() != 0 ? "len=" +  AgString::fromInt(preview.size()) : "null") + ", data: "
            + (data.size() != 0 ? "len=" + AgString::fromInt(data.size()) : "null") + "]";

    return ss;
}


void Data::setAttachments(std::map<AgString, AgObjectPtr > &attachments)
{
	this->attachments = attachments;
}


Data* Data::clone()
{
    Data* cl = new Data();

    cl->id = id;
    cl->name = name;
    cl->preview = preview;
    cl->data = data;

    return cl;
}

	
bool Data::equals(AgObject *obj)
{
    if (obj==NULL)
        return false;

    Data* other = dynamic_cast<Data*>(obj);
    if (other == this)
        return true;

    if (id != other->id)
        return false;

    if (name != other->name)
        return false;

    if (preview.size() != other->preview.size())
        return false;

    if (data.size() != other->preview.size())
        return false;

    bool previewEq = std::equal(this->preview.begin(), this->preview.end(), other->preview.begin());
    bool dataEq = std::equal(this->data.begin(), this->data.end(), other->data.begin());

    if (!previewEq || !dataEq)
        return false;

    return true;
}

int Data::compareTo(Comparable* /*obj*/)
{
    assert(0);
    return 0;
}

int Data::hashCode()
{
    assert(0);
    return 0;
}

std::string Data::getClass()
{
    return typeid(Data).name();
}
