/*									tab:4
 *
 *
 * "Copyright (c) 2000-2002 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose, without fee, and without written
 * agreement is hereby granted, provided that the above copyright
 * notice, the following two paragraphs and the author appear in all
 * copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE
 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */
/*									tab:4
 *									
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *  By downloading, copying, installing or using the software you
 *  agree to this license.  If you do not agree to this license, do
 *  not download, install, copy or use the software.
 *
 *  Intel Open Source License 
 *
 *  Copyright (c) 2002 Intel Corporation 
 *  All rights reserved. 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 * 
 *	Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *	Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *      Neither the name of the Intel Corporation nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL OR ITS
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 */

/*
 * Authors:   Philip Levis <pal@cs.berkeley.edu>
 *            Neil Patel
 * History:   Apr 11, 2003         Inception.
 *
 */

includes AM;
includes Bombilla;
includes BombillaMsgs;

module BombillaEngine {
  provides {
    interface StdControl;
    interface BombillaError;
    command result_t computeInstruction(BombillaContext* context);
    command result_t executeContext(BombillaContext* context);
    command result_t alertSendContext(BombillaContext* caller, BombillaStackVariable* var);
    command result_t alertReceiveContext(TOS_MsgPtr msg);
  }
  
  uses {
    interface StdControl as SubControlTimer;
    interface StdControl as SubControlNetwork;
    interface StdControl as SubControl;

    interface Timer as ClockTimer;
      
    interface Leds;
    interface Random;

    interface SendMsg as SendError;
    interface ReceiveMsg as ReceivePacket;
    
    interface BombillaStacks as Stacks;
    interface BombillaBuffer as Buffer;
    interface BombillaLocks as Locks;
    interface BombillaContextSynch as Synch;
    interface BombillaInstruction as Instruction;
    interface BombillaQueue as Queue;
    interface BombillaVirus as Virus;
    interface BombillaAnalysis as Analysis;
    
    interface BombillaBytecode as Bytecode[uint8_t bytecode] ;
  }
}


implementation {

  BombillaContext clockContext;
  BombillaContext sendContext;
  BombillaContext recvContext;
  //BombillaContext onceContext;
  BombillaState state;
  
  command result_t StdControl.init() {
    uint16_t i;

    dbg(DBG_BOOT, "VM: Bombilla initializing.\n");
    call Leds.init();
    call Random.init();

    call SubControlTimer.init();
    call SubControlNetwork.init();
    call SubControl.init();
    
    state.inErrorState = FALSE;

    clockContext.rootCapsule = &state.capsules[BOMB_CAPSULE_CLOCK_INDEX];
    clockContext.state = BOMB_STATE_HALT;
    sendContext.rootCapsule = &state.capsules[BOMB_CAPSULE_SEND_INDEX];
    sendContext.state = BOMB_STATE_HALT;
    recvContext.rootCapsule = &state.capsules[BOMB_CAPSULE_RECV_INDEX];
    recvContext.state = BOMB_STATE_HALT;
    /*
    onceContext.rootCapsule = &state.capsules[BOMB_CAPSULE_ONCE_INDEX];
    onceContext.state = BOMB_STATE_HALT; */
    
    call Queue.init(&state.readyQueue);

    for (i = 0; i < BOMB_BUF_NUM; i++) {
      state.buffers[i].type = BOMB_DATA_NONE;
      state.buffers[i].size = 0;
    }

    clockContext.which = BOMB_CAPSULE_CLOCK;
    sendContext.which = BOMB_CAPSULE_SEND;
    recvContext.which = BOMB_CAPSULE_RECV;
    /*
    onceContext.which = BOMB_CAPSULE_ONCE; */

    for (i = 0; i < BOMB_HEAPSIZE; i++) {
      state.heap[(int)i].type = BOMB_TYPE_VALUE;
      state.heap[(int)i].value.var = 0;
    }
    
    state.capsules[BOMB_CAPSULE_SUB0].capsule.type = BOMB_CAPSULE_SUB0;
    state.capsules[BOMB_CAPSULE_SUB1].capsule.type = BOMB_CAPSULE_SUB1;
    state.capsules[BOMB_CAPSULE_SUB2].capsule.type = BOMB_CAPSULE_SUB2;
    state.capsules[BOMB_CAPSULE_SUB3].capsule.type = BOMB_CAPSULE_SUB3;
    state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.type = BOMB_CAPSULE_CLOCK;
    state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.type = BOMB_CAPSULE_RECV;
    state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.type = BOMB_CAPSULE_SEND;
    state.capsules[BOMB_CAPSULE_ONCE_INDEX].capsule.type = BOMB_CAPSULE_ONCE;

    for (i = 0; i < BOMB_CAPSULE_NUM; i++) {
      state.capsules[i].capsule.type |= BOMB_OPTION_FORWARD;
      if (TOS_LOCAL_ADDRESS == 25 &&
          i == BOMB_CAPSULE_CLOCK_INDEX) {
        state.capsules[i].capsule.version = 1;
        state.capsules[i].capsule.type |= BOMB_OPTION_FORWARD;
      }
      else {
        state.capsules[i].capsule.version = 0;
      }
      call Virus.registerCapsule(state.capsules[i].capsule.type,
                                 &(state.capsules[i].capsule));
    }

    {
      int pc = 0;
      /*
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPpushc|1;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPsense;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPgetvar|1;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPadd;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPcopy;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPcopy;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPpushc|7;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPland;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPputled;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPsetvar|1;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPpushc|2;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbpush0;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPadd;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPadd;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbsize;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbpush0;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbtail;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbpush0;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbclear;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPhalt;
      */
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPpushc | 1;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPgetvar | 0;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPadd;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPcopy;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPcopy;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPsetvar | 0;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPpushc | 7;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPland;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPputled;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbpush0;  
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPbclear;  
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPadd;  
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPsendr;
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.code[pc++] = OPhalt;  
      state.capsules[BOMB_CAPSULE_CLOCK_INDEX].capsule.version = 0;

      pc = 0;
      state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.code[pc++] = OPpushc;
      state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.code[pc++] = OPlnot;
      state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.code[pc++] = OPsendr;
      state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.code[pc++] = OPhalt;
      state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.options = 0;
      state.capsules[BOMB_CAPSULE_SEND_INDEX].capsule.version = 0;

      pc = 0;
      //state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPpushc;
      //state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPbyank;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPbhead;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPpushc | 7;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPland;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPputled;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.code[pc++] = OPhalt;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.options = 0;
      state.capsules[BOMB_CAPSULE_RECV_INDEX].capsule.version = 0;
      
    }

    call Analysis.analyzeCapsuleVars(&state, BOMB_CAPSULE_CLOCK_INDEX);
    call Analysis.analyzeCapsuleVars(&state, BOMB_CAPSULE_SEND_INDEX);
    call Analysis.analyzeCapsuleVars(&state, BOMB_CAPSULE_RECV_INDEX);
    
    return SUCCESS;
  }

  task void ClockTask() {
    call computeInstruction(&clockContext);
    if (clockContext.state == BOMB_STATE_RUN) {
      dbg(DBG_USR1, "VM: Posting ClockTask in task.\n");
      post ClockTask();
    }
  }
  
  task void SendTask() {
    call computeInstruction(&sendContext);
    if (sendContext.state == BOMB_STATE_RUN) {
      dbg(DBG_USR1, "VM: Posting SendTask in task.\n");
      post SendTask();
    }
  }
  
  task void RecvTask() {
    call computeInstruction(&recvContext);
    if (recvContext.state == BOMB_STATE_RUN) {
      post RecvTask();
    }
  }
  /*
  task void OnceTask() {
    call computeInstruction(&onceContext);
    if (onceContext.state == BOMB_STATE_RUN) {
      post OnceTask();
    }
  }
  */
  command result_t executeContext(BombillaContext* context) {
    dbg(DBG_USR1, "VM: At beginning of execCon.\n");
    if (context->state != BOMB_STATE_RUN) {return FAIL;}
    switch(context->which) {
    case BOMB_CAPSULE_CLOCK:
      dbg(DBG_USR1, "VM: Posting ClockTask in execCon.\n");
      return post ClockTask();
      dbg(DBG_USR1, "VM: Posted ClockTask.\n");
      //return SUCCESS;
      dbg(DBG_USR1, "VM: after return.\n");
      break;
      dbg(DBG_USR1, "VM: after break.\n");
    case BOMB_CAPSULE_SEND:
      dbg(DBG_USR1, "VM: Posting SendTask in execCon.\n");
      post SendTask();
      return SUCCESS;
      break;
    case BOMB_CAPSULE_RECV:
      post RecvTask();
      return SUCCESS;
      break;
      /*
        case BOMB_CAPSULE_ONCE:
        return post OnceTask();
        case BOMB_CAPSULE_OUTER: */
    default:
      dbg(DBG_ERROR, "VM: Tried to run context not yet supported: %i\n", context->which);
      return FAIL;
    }
    dbg(DBG_USR1, "VM: At end of execCon.\n");
    return FAIL;
  }

  command result_t alertSendContext(BombillaContext* caller,
                                    BombillaStackVariable* arg) {
    if (sendContext.state == BOMB_STATE_HALT) {
      nmemcpy(&state.sendrBuffer, arg->buffer.var, sizeof(BombillaDataBuffer));
      call Synch.initializeContext(&sendContext, &state);
      call Stacks.pushBuffer(&sendContext, &state.sendrBuffer);
      dbg(DBG_USR1, "VM: Calling resumeCon for sendContext in alertSend. \n");
      call Synch.resumeContext(caller, &sendContext, &(state.readyQueue), state.locks);
      // This yield/resume allows locks to be yielded to the send context,
      // and if none are shared, then it will just continue executing
      if (caller->releaseSet) {
        call Synch.releaseLocks(caller, caller, state.locks);
        call Synch.yieldContext(caller, &(state.readyQueue), state.locks);
        //call Synch.resumeContext(caller, caller, &(state.readyQueue), state.locks);
      }
    }
    else {
      // Don't send, fail silently -- send context busy
    }
    return SUCCESS;
  }

  command result_t alertReceiveContext(TOS_MsgPtr msg) {
    if (!msg->crc) {return FAIL;}
    if (recvContext.state != BOMB_STATE_HALT) {
      dbg(DBG_USR1, "VM: Received packet, receive context busy, drop.\n");
    }
    else {
      int i;
      BombillaPacket* packet = (BombillaPacket*)(msg->data);
      BombillaDataBuffer* buffer = (BombillaDataBuffer*)&(packet->payload);
      dbg(DBG_USR1, "VM: Received packet, enqueuing receive context to run.\n");
      dbg(DBG_USR1, "VM: Copying %i bytes of header over:\n  ", sizeof(recvContext.header));
      for (i = 0; i < sizeof(recvContext.header); i++) {
        dbg_clear(DBG_USR1, "%02hhx", packet->header[i]);
      }
      dbg_clear(DBG_USR1, "\n->");
      nmemcpy(&(recvContext.header), packet->header, sizeof(recvContext.header));
      for (i = 0; i < sizeof(recvContext.header); i++) {
        dbg_clear(DBG_USR1, "%02hhx", ((uint8_t*)recvContext.header)[i]);
      }
      dbg_clear(DBG_USR1, "\n");
      nmemcpy(&(state.recvBuffer), buffer, sizeof(BombillaDataBuffer));
      call Synch.initializeContext(&recvContext, &state);
      call Stacks.pushBuffer(&recvContext, &state.recvBuffer);
      dbg(DBG_USR1, "VM: Calling resumeCon for recvCon in alertRec.\n");
      call Synch.resumeContext(&recvContext, &recvContext, &(state.readyQueue), state.locks);
    }
    return SUCCESS;
  }

  command result_t StdControl.start() {
    result_t res1, res2;
    dbg(DBG_BOOT, "VM: Starting.\n");
    res1 = call SubControlTimer.start();
    res2 = call SubControlNetwork.start();
    res2 = call SubControl.start();
    call ClockTimer.start(TIMER_REPEAT, 1024);
    return rcombine(res1, res2);
  }

  command result_t StdControl.stop() {
    result_t res1, res2;
    dbg(DBG_BOOT, "VM: Stopping.\n");
    call ClockTimer.stop();
    res1 = call SubControlTimer.stop();
    res2 = call SubControlNetwork.stop();
    res2 = call SubControl.stop();
    return rcombine(res1, res2);
  }
  
  command result_t BombillaError.error(BombillaContext* context, uint8_t cause) {
    state.inErrorState = TRUE;
    dbg(DBG_ERROR|DBG_USR1, "VM: Entering ERROR state. Context: %i, cause %i\n", (int)context->which, (int)cause);
    call Leds.redOn();
    call Leds.greenOn();
    call Leds.yellowOn();
    state.errorContext = context;
    if (context != NULL) {
      state.errorContext = context;
      state.errorMsg.context = context->which;
      state.errorMsg.reason = cause;
      state.errorMsg.capsule = context->capsule->capsule.type;
      state.errorMsg.instruction = context->pc - 1;
      context->state = BOMB_STATE_HALT;
    }
    else {
      state.errorMsg.context = BOMB_CAPSULE_INVALID;
      state.errorMsg.reason = cause;
      state.errorMsg.capsule = BOMB_CAPSULE_INVALID;
      state.errorMsg.instruction = 255;
    }
    return SUCCESS;
  }

  task void ClockErrorTask() {
    dbg(DBG_USR1|DBG_ERROR, "VM: ERROR\n");
    call Leds.redToggle();
    call Leds.greenToggle();
    call Leds.yellowToggle();
    nmemcpy(state.errorContext->msg.data,
	    &state.errorMsg,
	    sizeof(BombillaErrorMsg));

    if (state.errorFlipFlop) {
      call SendError.send(TOS_UART_ADDR,
			  sizeof(BombillaErrorMsg),
			  (TOS_MsgPtr)&(state.errorContext->msg));
    }
    else {
      call SendError.send(TOS_BCAST_ADDR,
			  sizeof(BombillaErrorMsg),
			  (TOS_MsgPtr)&(state.errorContext->msg));
    }
    state.errorFlipFlop = !state.errorFlipFlop;
  }
    
  task void ClockEventTask() {
    if (clockContext.state == BOMB_STATE_HALT) {
      call Synch.initializeContext(&clockContext, &state);
      clockContext.state = BOMB_STATE_RUN;
      //initializeContext(&clockContext);
      dbg(DBG_USR1, "VM: Calling ResumeCon for clockCon in ClockEventTask.\n");
      call Synch.resumeContext(&clockContext, &clockContext, &(state.readyQueue), state.locks);
      //resumeContext(&clockContext, &clockContext);
    }
    else {
      dbg(DBG_TEMP, "VM: Clock context not halted.\n");
      // Can log a clock miss error here, but probably
      // not a good idea
    }
  }

  event result_t ClockTimer.fired() {
    dbg(DBG_USR1, "VM: clock timer fired, posting ClockEventTask.\n");
    if (state.inErrorState) {post ClockErrorTask();}
    else {post ClockEventTask();}
    return SUCCESS;
  }

  event result_t SendError.sendDone(TOS_MsgPtr msg, result_t success) {
    return SUCCESS;
  }
  
  event TOS_MsgPtr ReceivePacket.receive(TOS_MsgPtr msg) {
    return msg;
  }
  
  command result_t computeInstruction(BombillaContext* context) {
    uint8_t instr = context->capsule->capsule.code[(int)context->pc];
    //	dbg(DBG_USR1, "VM (%hhi): Issuing instruction 0x%hhx.\n", context->which, instr);
    if (context->state != BOMB_STATE_RUN) {
      dbg(DBG_ERROR, "VM: (%hhi) Tried to execute instruction in non-run state: %hhi\n", context->which, context->state);
      return FAIL;
    }
    context->pc++;
    call Bytecode.execute[instr](instr, context, &state);
    return SUCCESS;  
  }

  default command result_t Bytecode.execute[uint8_t opcode](uint8_t instr,
							    BombillaContext* context,
							    BombillaState* vmState) {
    dbg(DBG_ERROR|DBG_USR1, "VM: Executing default instruction: halt!\n");
    context->state = BOMB_STATE_HALT;
    return FAIL;
  }
  
  event result_t Virus.capsuleInstalled(BombillaCapsule* capsule) {
    //capsuleAnalysis(capsule->type & BOMB_OPTION_MASK);
    return SUCCESS;
  }

  event result_t Virus.enableExecution() {
    return SUCCESS;
  }

  event result_t Virus.disableExecution() {
    return SUCCESS;
  }

  event result_t Virus.capsuleHeard(uint8_t type) {
    return SUCCESS;
  }

  event void Virus.capsuleForce(uint8_t type) {
    return;
  }

  event result_t Synch.makeRunnable(BombillaContext* context) {
    dbg(DBG_USR1, "VM: make runnable event occured for %i.\n", (int)context->which);
    context->state = BOMB_STATE_RUN;
    dbg(DBG_USR1, "VM: calling execCon.\n");
    return call executeContext(context);
  }

}
