/*
 * 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.
 *
 */
#ifndef __RCX_Cmd_h
#define __RCX_Cmd_h

#ifndef __PTypes_h
#include "PTypes.h"
#endif

#ifndef __RCX_Constants_h
#include "RCX_Constants.h"
#endif

#define kRCX_Cmd_MaxShortLength 8


class RCX_Cmd
{
public:
    RCX_Cmd() { fLength = 0; };
    ~RCX_Cmd();

    int GetLength() const {
        return fLength;
    }

    UByte* GetBody() {
        return (fLength <= kRCX_Cmd_MaxShortLength) ? fBody.fData : fBody.fPtr;
    }
    const UByte* GetBody() const {
        return (fLength <= kRCX_Cmd_MaxShortLength) ? fBody.fData : fBody.fPtr;
    }

    int CopyOut(UByte *dst);

    void SetLength(int length);
    UByte& operator[](int index) {
        return GetBody()[index];
    }
    const UByte& operator[](int index) const {
        return GetBody()[index];
    }

    RCX_Cmd* Set(const UByte *data, int length);
    RCX_Cmd* Set(UByte d0);
    RCX_Cmd* Set(UByte d0, UByte d1);
    RCX_Cmd* Set(UByte d0, UByte d1, UByte d2);
    RCX_Cmd* Set(UByte d0, UByte d1, UByte d2, UByte d3);
    RCX_Cmd* Set(UByte d0, UByte d1, UByte d2, UByte d3, UByte d4);
    RCX_Cmd* Set(UByte d0, UByte d1, UByte d2, UByte d3, UByte d4,
        UByte d5);
    RCX_Cmd* Set(UByte d0, UByte d1, UByte d2, UByte d3, UByte d4,
        UByte d5, UByte d6);
    RCX_Cmd* Set(UByte d0, UByte d1, UByte d2, UByte d3, UByte d4,
        UByte d5, UByte d6, UByte d7);

    // utility functions to create specific commands
    // variables
    RCX_Cmd* MakeVar(RCX_VarCode code, UByte var, RCX_Value value);
    RCX_Cmd* MakeVarNewSrc(RCX_VarCode code, UByte var, RCX_Value value);

    // outputs
    RCX_Cmd* MakeOutputMode(UByte outputs, RCX_OutputMode mode) {
        return Set(kRCX_OutputModeOp, (UByte)(mode | outputs));
    }
    RCX_Cmd* MakeOutputPower(UByte outputs, RCX_Value value);
    RCX_Cmd* MakeOutputDir(UByte outputs, RCX_OutputDir dir) {
        return Set(kRCX_OutputDirOp, (UByte)(dir | outputs));
    }

    // inputs
    RCX_Cmd* MakeInputMode(UByte input, RCX_InputMode mode) { 
        return Set(kRCX_InputModeOp, input, mode);
    }
    RCX_Cmd* MakeInputType(UByte input, RCX_InputType type) {
        return Set(kRCX_InputTypeOp, input, type);
    }

    // sound
    RCX_Cmd* MakePlaySound(UByte sound) {
        return Set(kRCX_PlaySoundOp, (UByte)(sound & 7));
    }
    RCX_Cmd* MakePlayTone(UShort freq, UByte duration);

    // control flow
    RCX_Cmd* MakeTest(RCX_Value v1, RCX_Relation rel, RCX_Value v2, short offset);
    RCX_Cmd* MakeJump(short offset);
    RCX_Cmd* MakeSetLoop(RCX_Value v) {
        return MakeValue8(kRCX_SetLoopOp, v);
    }
    RCX_Cmd* MakeCheckLoop(short offset);

    // misc
    RCX_Cmd* MakeStopTask(UByte task) {
        return Set(kRCX_StopTaskOp, task);
    }
    RCX_Cmd* MakeDelay(RCX_Value value) {
        return MakeValue16(kRCX_DelayOp, value);
    }
    RCX_Cmd* MakeDisplay(RCX_Value value) {
        return MakeValue16(kRCX_DisplayOp, value);
    }
    RCX_Cmd* MakeSet(RCX_Value dst, RCX_Value src);
    RCX_Cmd* MakeSetMath(RCX_Value dst, RCX_Value src, RCX_VarCode op);
    RCX_Cmd* MakePushStackEntry(RCX_Value value) {
        return MakeValue16(kRCX_PushStackEntryOp, value);
    }

    // system commands
    RCX_Cmd* MakeRead(RCX_Value value) {
        return MakeValue8(kRCX_ReadOp, value);
    }
    RCX_Cmd* MakeUnlock();
    RCX_Cmd* MakeGetVersions();
    RCX_Cmd* MakeBegin(RCX_ChunkType type, UByte taskNumber, UShort length);
    RCX_Cmd* MakeDownload(UShort seq, const UByte *data, UShort length);
    RCX_Cmd* MakeDeleteTasks() { return Set(kRCX_DeleteTasksOp); }
    RCX_Cmd* MakeDeleteSubs() { return Set(kRCX_DeleteSubsOp); }
    RCX_Cmd* MakePing() { return Set(kRCX_PingOp); }
    RCX_Cmd* MakeUploadDatalog(UShort start, UShort count);
    RCX_Cmd* MakeBoot();

    // special command to unlock CyberMaster
    RCX_Cmd* MakeUnlockCM();

    void SetOffset(int offset);
    void Print();

private:
    RCX_Cmd* MakeValue16(UByte opcode, RCX_Value value);
    RCX_Cmd* MakeValue8(UByte opcode, RCX_Value value);

    int fLength;
    union {
        UByte   fData[kRCX_Cmd_MaxShortLength];
        UByte*  fPtr;
    } fBody;
};


#endif
