/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * The Initial Developer of this code is David Baum.
 * Portions created by David Baum are Copyright (C) 1999 David Baum.
 * All Rights Reserved.
 *
 * Portions created by John Hansen are Copyright (C) 2005 John Hansen.
 * All Rights Reserved.
 *
 */
#include "Fragment.h"
#include "BlockStmt.h"
#include "RCX_Cmd.h"
#include "Bytecode.h"
#include "Program.h"
#include "Symbol.h"
#include "CallStmt.h"
#include "DeclareStmt.h"
#include "TaskIdExpr.h"
#include "GotoStmt.h"

#ifdef DEBUG
//#ifdef __MWERKS__
#include <typeinfo>
//#endif
// uncomment this to print the statement tree
//#define DEBUG_DUMP_FRAGMENT
#endif


Fragment::Fragment(bool isTask, Symbol *name, Stmt *body)
{
    fIsTask = isTask;
    fName = name;
    fBody = body;

    fNumber = gProgram->AddFragment(this);
    fTaskID = isTask ? fNumber : kNoTaskID;

    fStart.fIndex = kIllegalSrcIndex;
    fEnd.fIndex = kIllegalSrcIndex;
}

Fragment::Fragment(bool isTask) : fName(0), fBody(0)
{
        fIsTask = isTask;

        fStart.fIndex = kIllegalSrcIndex;
        fEnd.fIndex = kIllegalSrcIndex;
}


Fragment::~Fragment()
{
    delete fBody;
}


void Fragment::SetName(Symbol *name)
{
        fName = name;
        fNumber = gProgram->AddFragment(this);
        fTaskID = fIsTask ? fNumber : kNoTaskID;
}

void Fragment::SetBody(Stmt *s)
{
//        delete fBody; // this caused a call to a pure virtual function...
        fBody = s;
}

bool Fragment::AddArg(const Symbol *name, FunctionDef::ArgType type)
{
        Arg a;

        for(size_t i=0; i<fArgs.size(); i++) {
                if (fArgs[i].fName == name) {
                    return false;
                }
        }

        a.fName = name;
        a.fType = type;

        fArgs.push_back(a);
        return true;
}

void Fragment::CreateArgVars()
{
    // if a fragment has arguments then it must be
    // a target which supports the stack so
    // get the variable off the stack
    for(size_t i=0; i<fArgs.size(); i++) {
        int var;

         var = gProgram->NextVirtualVar();

        switch(fArgs[i].fType) {
            case FunctionDef::kConstantArg:
                var |= kVirtualConstantFlag | kVirtualReadOnlyFlag;
                break;
            case FunctionDef::kConstRefArg:
            case FunctionDef::kSensorArg:
                var |= kVirtualReadOnlyFlag;
                break;
            case FunctionDef::kPointerArg:
                var |= kPointerFlag;
                break;
            case FunctionDef::kConstPtrArg:
                var |= kVirtualReadOnlyFlag | kPointerFlag;
                break;
            default:
                break;
        }

        fArgs[i].fVar = var;
        gProgram->DefineVar(fArgs[i].fName, var, false,
            ((fArgs[i].fType == FunctionDef::kPointerArg) ||
                (fArgs[i].fType == FunctionDef::kConstPtrArg)),
                    true);
    }
}


void Fragment::SetLocations(LocationNode *start, LocationNode *end)
{
    fStart = start->GetLoc();
    delete start;

    fEnd = end->GetLoc();
    delete end;
}


void Fragment::AssignTaskID(int n)
{
    if (n == fTaskID) return;

    if (fTaskID == kNoTaskID)
        fTaskID = n;
    else
        fTaskID = kMultiTaskID;
}


void Fragment::Emit(Bytecode &b)
{
    b.AddSourceTag(RCX_SourceTag::kBegin, fStart);

    // bind TaskIdExprs to the actual task id
    // this must be done in Emit() rather than
    // Check() since subroutine trace isn't
    // complete until all fragments are checked
    TaskIdExpr::Patcher p(fTaskID);
    Apply(fBody, p);


    // resolve gotos - must be done in Emit() rather than
    // Check() since bytecode labels need to be generated
    GotoStmt::ResolveGotos(fBody, b);

    int rLabel = b.PushFlow(Bytecode::kReturnFlow);

    fBody->Emit(b);

    b.SetLabel(rLabel);
    b.PopFlow(Bytecode::kReturnFlow);

    b.ApplyFixups();

    b.AddSourceTag(RCX_SourceTag::kEnd, fEnd);
}


#ifdef DEBUG

using std::printf;

class ParentChecker
{
public:
    bool operator()(Stmt *s);
};

// Just eat the warnings about format specifiers in DEBUG mode.
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wformat"
#endif
bool ParentChecker::operator()(Stmt *s)
{
    Stmt *c;
    for(c=s->GetChildren(); c; c=c->GetNext()) {
        if (c->GetParent() != s) {
            printf("%s  %08x\n", typeid(*s).name(), s);
            printf("Parent mistmatch: Found %08x expected %08x\n",
                c->GetParent(), s);
        }
    }

    return true;
}
#endif


void Fragment::Check()
{
#ifdef DEBUG_DUMP_FRAGMENT
    DumpStmt(fBody);
#endif

    CallStmt::Expander e(this);
    Apply(fBody, e);

    DeclareStmt::Binder b(0);
    Apply(fBody, b);


#ifdef DEBUG
    ParentChecker p;
    Apply(fBody, p);
#endif

#ifdef DEBUG_DUMP_FRAGMENT
    DumpStmt(fBody);
#endif
}
