// Copyright 2008 Google Inc.
// Author: Lincoln Smith
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// 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.

#ifndef OPEN_VCDIFF_DECODETABLE_H_
#define OPEN_VCDIFF_DECODETABLE_H_

#include <config.h>
#include <stddef.h>     // NULL
#include <stdint.h>     // int32_t
#include <memory>       // auto_ptr
#include "codetable.h"  // VCDiffInstructi...
#include "logging.h"

namespace open_vcdiff {

// This class is used by the decoder.  It can use a standard or
// non-standard code table, and will translate the opcodes in the code table
// into delta instructions.
//
// NOT threadsafe.
//
class VCDiffCodeTableReader {
 public:
  // When constructed, the object will be set up to use the default code table.
  // If a non-default code table is to be used, then UseCodeTable()
  // should be called after the VCDiffCodeTableReader has been constructed.
  // In any case, the Init() method must be called before GetNextInstruction()
  // may be used.
  //
  VCDiffCodeTableReader();

  // Sets up a non-standard code table.  The caller
  // may free the memory occupied by the argument code table after
  // passing it to this method, because the argument code table
  // allocates space to store a copy of it.
  // UseCodeTable() may be called either before or after calling Init().
  // Returns true if the code table was accepted, or false if the
  // argument did not appear to be a valid code table.
  //
  bool UseCodeTable(const VCDiffCodeTableData& code_table_data,
                    unsigned char max_mode);

  // Defines the buffer containing the instructions and sizes.
  // This method must be called before GetNextInstruction() may be used.
  // Init() may be called any number of times to reset the state of
  // the object.
  //
  void Init(const char** instructions_and_sizes,
            const char* instructions_and_sizes_end) {
    instructions_and_sizes_ = instructions_and_sizes;
    instructions_and_sizes_end_ = instructions_and_sizes_end;
    last_instruction_start_ = NULL;
    pending_second_instruction_ = kNoOpcode;
    last_pending_second_instruction_ = kNoOpcode;
  }

  // Updates the pointers to the buffer containing the instructions and sizes,
  // but leaves the rest of the reader state intact, so that (for example)
  // any pending second instruction or unread instruction will still be
  // read when requested.  NOTE: UnGetInstruction() will not work immediately
  // after using UpdatePointers(); GetNextInstruction() must be called first.
  //
  void UpdatePointers(const char** instructions_and_sizes,
                      const char* instructions_and_sizes_end) {
    instructions_and_sizes_ = instructions_and_sizes;
    instructions_and_sizes_end_ = instructions_and_sizes_end;
    last_instruction_start_ = *instructions_and_sizes;
    // pending_second_instruction_ is unchanged
    last_pending_second_instruction_ = pending_second_instruction_;
  }

  // Returns the next instruction from the stream of opcodes,
  // or VCD_INSTRUCTION_END_OF_DATA if the end of the opcode stream is reached,
  // or VCD_INSTRUCTION_ERROR if an error occurred.
  // In the first of these cases, increments *instructions_and_sizes_
  // past the values it reads, and populates *size
  // with the corresponding size for the returned instruction;
  // otherwise, the value of *size is undefined, and is not
  // guaranteed to be preserved.
  // If the instruction returned is VCD_COPY, *mode will
  // be populated with the copy mode; otherwise, the value of *mode
  // is undefined, and is not guaranteed to be preserved.
  // Any occurrences of VCD_NOOP in the opcode stream
  // are skipped over and ignored, not returned.
  // If Init() was not called before calling this method, then
  // VCD_INSTRUCTION_ERROR will be returned.
  //
  VCDiffInstructionType GetNextInstruction(int32_t* size, unsigned char* mode);

  // Puts a single instruction back onto the front of the
  // instruction stream.  The next call to GetNextInstruction()
  // will return the same value that was returned by the last
  // call.  Calling UnGetInstruction() more than once before calling
  // GetNextInstruction() will have no additional effect; you can
  // only rewind one instruction.
  //
  void UnGetInstruction() {
    if (last_instruction_start_) {
      if (last_instruction_start_ > *instructions_and_sizes_) {
        VCD_DFATAL << "Internal error: last_instruction_start past end of "
                      "instructions_and_sizes in UnGetInstruction" << VCD_ENDL;
      }
      *instructions_and_sizes_ = last_instruction_start_;
      if ((pending_second_instruction_ != kNoOpcode) &&
          (last_pending_second_instruction_ != kNoOpcode)) {
        VCD_DFATAL << "Internal error: two pending instructions in a row "
                      "in UnGetInstruction" << VCD_ENDL;
      }
      pending_second_instruction_ = last_pending_second_instruction_;
    }
  }

 private:
  // A pointer to the code table.  This is the object that will be used
  // to interpret opcodes in GetNextInstruction().
  const VCDiffCodeTableData* code_table_data_;

  // If the default code table is not being used, then space for the
  // code table data will be allocated using this pointer and freed
  // when the VCDiffCodeTableReader is destroyed.  This will keep the
  // code that uses the object from having to worry about memory
  // management for the non-standard code table, whose contents have
  // been read as part of the encoded data file/stream.
  //
  std::auto_ptr<VCDiffCodeTableData> non_default_code_table_data_;

  const char** instructions_and_sizes_;
  const char* instructions_and_sizes_end_;
  const char* last_instruction_start_;
  OpcodeOrNone pending_second_instruction_;
  OpcodeOrNone last_pending_second_instruction_;

  // Making these private avoids implicit copy constructor & assignment operator
  VCDiffCodeTableReader(const VCDiffCodeTableReader&);
  void operator=(const VCDiffCodeTableReader&);
};

};  // namespace open_vcdiff

#endif  // OPEN_VCDIFF_DECODETABLE_H_
