/**
 * Tencent is pleased to support the open source community by making Tars available.
 *
 * Copyright (C) 2016THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except 
 * in compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * 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 "tars2php.h"
#include "util/tc_md5.h"
#include "util/tc_file.h"
#include "util/tc_common.h"
#include <string>

#define TAB g_parse->getTab()
#define INC_TAB g_parse->incTab()
#define DEL_TAB g_parse->delTab()

//////////////////////////////////////////////////////////////////////////////////
//
Tars2Php::Tars2Php(): m_bCheckDefault(false)
{

}

string Tars2Php::writeTo(const TypeIdPtr &pPtr) const
{
    ostringstream s;
    s << TAB << "$this->" << pPtr->getId() << "->write($_out," << pPtr->getTag() << ");" << endl;
    return s.str();
}
string Tars2Php::readFrom(const TypeIdPtr &pPtr, bool bIsRequire) const
{
    ostringstream s;
    s<<TAB<<"$this->"<<pPtr->getId()<<"->read($_in ";
    s << ", " << pPtr->getTag() << ", " << ((pPtr->isRequire() && bIsRequire)?"true":"false") << ");" << endl;
    return s.str();
}

int Tars2Php::getSuffix(const TypeIdPtr &pPtr) const
{
    BuiltinPtr bPtr = BuiltinPtr::dynamicCast(pPtr->getTypePtr());
    if(bPtr && bPtr->kind() == Builtin::KindString && bPtr->isArray())
    {
        return bPtr->getSize();
    }

    VectorPtr vPtr = VectorPtr::dynamicCast(pPtr->getTypePtr());
    if(vPtr && vPtr->isArray())
    {
        return vPtr->getSize();
    }

    return -1;
}

string Tars2Php::toStrSuffix(const TypeIdPtr &pPtr) const
{
    ostringstream s;

    int i = getSuffix(pPtr);

    if(i >= 0)
    {
         s << "[" << i << "]";
    }
    return s.str();     
}
/*******************************BuiltinPtr********************************/

string Tars2Php::tostr(const TypePtr &pPtr) const
{
    BuiltinPtr bPtr = BuiltinPtr::dynamicCast(pPtr);
    if(bPtr) return tostrBuiltin(bPtr);

    VectorPtr vPtr = VectorPtr::dynamicCast(pPtr);
    if(vPtr) return tostrVector(vPtr);

    MapPtr mPtr = MapPtr::dynamicCast(pPtr);
    if(mPtr) return tostrMap(mPtr);

    StructPtr sPtr = StructPtr::dynamicCast(pPtr);
    if(sPtr) return tostrStruct(sPtr);

    EnumPtr ePtr = EnumPtr::dynamicCast(pPtr);
    if(ePtr) return tostrEnum(ePtr);

    if(!pPtr) return "void";

    assert(false);
    return "";
}

string Tars2Php::tostrBuiltin(const BuiltinPtr &pPtr) const
{
    string s;

    switch(pPtr->kind())
    {
    case Builtin::KindBool:
        s = "c_char";
        break;
    case Builtin::KindByte:
        s = (pPtr->isUnsigned()?"c_short":"c_char");
        break;
    case Builtin::KindShort: 
        s = (pPtr->isUnsigned()?"c_short":"c_short");
        break;
    case Builtin::KindInt:
        s = (pPtr->isUnsigned()?"c_int":"c_int");
        break;
    case Builtin::KindLong:
        s =  "c_int64";
        break;
    case Builtin::KindFloat:
        s = "c_float";
        break;
    case Builtin::KindDouble:
        s = "c_double";
        break;
    case Builtin::KindString:
        s = "c_string";
        break;
    case Builtin::KindVector:
        s = "c_vector";
        break;
    case Builtin::KindMap:
        s = "c_map";
        break;
    default:
        assert(false);
        break;
    }

    return s;
}
/*******************************VectorPtr********************************/
string Tars2Php::tostrVector(const VectorPtr &pPtr) const
{
    string s = string("c_vector (new ") + tostr(pPtr->getTypePtr()) + string(")");
    return s;
}
/*******************************MapPtr********************************/
string Tars2Php::tostrMap(const MapPtr &pPtr) const
{
    string s = string("c_map (new ") + tostr(pPtr->getLeftTypePtr()) + ",new " + tostr(pPtr->getRightTypePtr()) + string(")");
    return s;
}

/*******************************StructPtr********************************/
string Tars2Php::tostrStruct(const StructPtr &pPtr) const
{
    return pPtr->getId();
}


/////////////////////////////////////////////////////////////////////
string Tars2Php::tostrEnum(const EnumPtr &pPtr) const
{
    return "c_int";//pPtr->getId();
}
///////////////////////////////////////////////////////////////////////
string Tars2Php::generatePHP(const StructPtr &pPtr, const string& namespaceId) const
{
    ostringstream s;

	vector<TypeIdPtr>& member = pPtr->getAllMemberPtr();

    s << TAB << "class " << pPtr->getId() << " extends c_struct" << endl;
    s << TAB << "{" << endl;
    INC_TAB;
    for(size_t k = 0;k < member.size();k++)
    {
        s<< TAB << "public $" <<member[k]->getId()<<";"<<endl;
    }
    s<<endl;
    s << TAB << "public function __clone()" << endl;
    s << TAB << "{" << endl;
    INC_TAB;
    for(size_t k = 0;k < member.size();k++) 
    {
        s<< TAB << "$this->" <<member[k]->getId()<<" = clone $this->"<<member[k]->getId()<<";"<<endl;
    }
    DEL_TAB;
    s << TAB << "}" << endl;
    s <<endl;
    
    s << TAB << "public function __construct()" << endl;
    s << TAB << "{" << endl;
    INC_TAB;
    for(size_t k = 0;k < member.size();k++) 
    {
        s<< TAB << "$this->" <<member[k]->getId()<<" = new  "<< tostr(member[k]->getTypePtr())<<";"<<endl;
    }
    DEL_TAB;
    s << TAB << "}" << endl;
    s <<endl;
    ////////////////////////////////////////////////////////////
    s << TAB << "public function get_class_name()" << endl;
    s << TAB << "{" << endl;
    INC_TAB;
    s << TAB << "return " << "\"" << namespaceId << "." << pPtr->getId() << "\"" << ";" << endl;
    DEL_TAB;
    s << TAB << "}" << endl;
    s<<endl;

	//write begin
    s << TAB << "public function write(&$_out,$tag)" << endl;
    s << TAB << "{" << endl;
    INC_TAB;
    s << TAB << "tars_header::_pack_header($_out,'c_struct_begin',$tag);" << endl;

    for(size_t j = 0; j < member.size(); j++)
    {
        s << writeTo(member[j]);
    }

    s << TAB << "tars_header::_pack_header($_out,'c_struct_end',0);" << endl;

    DEL_TAB;
    s << TAB << "}" << endl;
	//write end

	///read begin
    s << TAB << "public function read(&$_in,$tag,$isRequire = true)" << endl;
    s << TAB << "{" << endl;

    INC_TAB;

    s << TAB << "tars_header::_check_struct($_in,$type,$tag,$isRequire);"<<endl;
    s << TAB << "tars_header::_unpack_header($_in,$type,$this_tag);"<<endl;

    for(size_t j = 0; j < member.size(); j++)
    {
        s << readFrom(member[j]);
    }

    s<< TAB << "$this->_skip_struct($_in);" << endl;

    DEL_TAB;
    s << TAB << "}" << endl;
	//read end

    DEL_TAB;
    s << TAB << "}" << endl;
    return s.str();
}


/******************************NamespacePtr***************************************/
string Tars2Php::generatePHP(const NamespacePtr &pPtr) const
{
    ostringstream s;

    vector<StructPtr> &ss = pPtr->getAllStructPtr();

    for(size_t i = 0; i < ss.size(); i++)
    {
        s << generatePHP(ss[i], pPtr->getId()) << endl;
    }

    return s.str();
}

void Tars2Php::generatePHP(const ContextPtr &pPtr) const
{
    string n        = tars::TC_File::excludeFileExt(tars::TC_File::extractFileName(pPtr->getFileName()));
    string fileH    = m_sBaseDir + FILE_SEP + n + "_wup.php";

    string define = "<?php";

    ostringstream s;
    s << define << endl;
    s << endl;
    s << g_parse->printHeaderRemark();
    s << endl;

    s << "require_once('tars.php');" <<endl;
	s <<endl;

    vector<NamespacePtr> namespaces = pPtr->getNamespaces();

    for(size_t i = 0; i < namespaces.size(); i++)
    {
        s << generatePHP(namespaces[i]) << endl;
    }

    s << "?>"<<endl;

    tars::TC_File::makeDirRecursive(m_sBaseDir);
    tars::TC_File::save2file(fileH, s.str());
}

void Tars2Php::generatePHP_Pdu(const ContextPtr &pPtr) const
{
    string n        = tars::TC_File::excludeFileExt(tars::TC_File::extractFileName(pPtr->getFileName()));
    string fileH    = m_sBaseDir + FILE_SEP + n + "_pdu.php";

    string define = "<?php";

    ostringstream s;
    s << define << endl;
    s << endl;
    s << g_parse->printHeaderRemark();
    s << endl;
    s << "require_once('tars.php');" <<endl;

    vector<NamespacePtr> namespaces = pPtr->getNamespaces();

    for(size_t i = 0; i < namespaces.size(); i++)
    {
        s << generatePHP(namespaces[i]) << endl;
    }
    s << "?>";

    tars::TC_File::makeDirRecursive(m_sBaseDir);
    tars::TC_File::save2file(fileH, s.str());
}

void Tars2Php::createFile(const string &file, const vector<string> &vsCoder)
{
    std::vector<ContextPtr> contexts = g_parse->getContexts();
    for(size_t i = 0; i < contexts.size(); i++)
    {
        if(file == contexts[i]->getFileName())
        {
            generatePHP(contexts[i]);
        }
    }
}



