#ifndef STICK20_COMMANDS_H
#define STICK20_COMMANDS_H
#include "inttypes.h"
#include "command.h"
#include <string>
#include <sstream>
#include "device_proto.h"


namespace nitrokey {
namespace proto {

/*
*	STICK20 protocol command ids
*	a superset (almost) of STICK10
*/
namespace stick20 {

    enum class PasswordKind : uint8_t {
        User = 'P',
        Admin = 'A'
    };

    class ChangeAdminUserPin20Current : Command<CommandID::STICK20_CMD_SEND_PASSWORD> {
  public:
      struct CommandPayload {
          uint8_t kind;
          uint8_t old_pin[20];
          std::string dissect() const {
            std::stringstream ss;
            ss << " old_pin:\t" <<  old_pin<< std::endl;
            return ss.str();
          }
          void set_kind(PasswordKind k){
            kind = (uint8_t)k;
          }
      } __packed;

      typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
              CommandTransaction;
  };


    class ChangeAdminUserPin20New : Command<CommandID::STICK20_CMD_SEND_NEW_PASSWORD> {
    public:

        struct CommandPayload {
            uint8_t kind;
            uint8_t new_pin[20];
            std::string dissect() const {
              std::stringstream ss;
              ss << " new_pin:\t" << new_pin<< std::endl;
              return ss.str();
            }
            void set_kind(PasswordKind k){
              kind = (uint8_t)k;
            }

        } __packed;

        typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
                CommandTransaction;
    };


    class UnlockUserPassword : Command<CommandID::UNLOCK_USER_PASSWORD> {
    public:
        struct CommandPayload {
            uint8_t kind;
            uint8_t user_new_password[20];
            std::string dissect() const {
              std::stringstream ss;
              ss << " user_new_password:\t" <<  user_new_password<< std::endl;
              return ss.str();
            }
            void set_kind(PasswordKind k){
              kind = (uint8_t)k;
            }
        } __packed;

        typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
            CommandTransaction;
    };

class EnableEncryptedPartition : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];  // TODO check w/ firmware
  };

  typedef Transaction<CommandID::ENABLE_CRYPTED_PARI, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class DisableEncryptedPartition : semantics::non_constructible {
 public:
  typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, struct EmptyPayload,
                      struct EmptyPayload> CommandTransaction;
};

class EnableHiddenEncryptedPartition : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];  // TODO check w/ firmware
  };

  typedef Transaction<CommandID::ENABLE_HIDDEN_CRYPTED_PARI,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class DisableHiddenEncryptedPartition : semantics::non_constructible {
 public:
  typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, struct EmptyPayload,
                      struct EmptyPayload> CommandTransaction;
};

class EnableFirmwareUpdate : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];  // TODO check w/ firmware
  };

  typedef Transaction<CommandID::ENABLE_FIRMWARE_UPDATE, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class UpdatePassword : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t old_password[15];
    uint8_t new_password[15];
  };

  typedef Transaction<CommandID::CHANGE_UPDATE_PIN, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class ExportFirmware : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::EXPORT_FIRMWARE_TO_FILE, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

    class CreateNewKeys : Command<CommandID::GENERATE_NEW_KEYS> {
    public:
        struct CommandPayload {
            uint8_t kind;
            uint8_t admin_password[30]; //CS20_MAX_PASSWORD_LEN
            std::string dissect() const {
              std::stringstream ss;
              ss << " admin_password:\t" <<  admin_password<< std::endl;
              return ss.str();
            }
            void setKindPrefixed(){
              kind = 'P';
            }
        } __packed;

        typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
            CommandTransaction;
    };


class FillSDCardWithRandomChars : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t volume_flag;
    uint8_t password[30];
  };

  typedef Transaction<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SetupHiddenVolume : semantics::non_constructible {
 public:
  typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP, struct EmptyPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendPasswordMatrix : semantics::non_constructible {
 public:
  typedef Transaction<CommandID::SEND_PASSWORD_MATRIX, struct EmptyPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendPasswordMatrixPinData : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t pin_data[30];  // TODO how long actually can it be?
  };

  typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_PINDATA,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendPasswordMatrixSetup : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t setup_data[30];  // TODO how long actually can it be?
  };

  typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_SETUP,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

#define d(x) ss << " "#x":\t" << (int)x << std::endl;

    class GetDeviceStatus : Command<CommandID::GET_DEVICE_STATUS> {
    public:
        static const int OUTPUT_CMD_RESULT_STICK20_STATUS_START = 20 +1;
        static const int payload_absolute_begin = 8;
        static const int padding_size = OUTPUT_CMD_RESULT_STICK20_STATUS_START - payload_absolute_begin;
        struct ResponsePayload {
            uint8_t _padding[padding_size]; //TODO confirm padding in Storage firmware
            //data starts from 21st byte of packet -> 13th byte of payload
            uint8_t command_counter;
            uint8_t last_command;
            uint8_t status;
            uint8_t progress_bar_value;
            bool isValid() const { return true; }

            std::string dissect() const {
              std::stringstream ss;
                d(command_counter);
                d(last_command);
                d(status);
                d(progress_bar_value);
              ss << "_padding:\t"
                 << ::nitrokey::misc::hexdump((const char *)(_padding),
                                              sizeof _padding);
              return ss.str();
            }
        } __packed;

        typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
                CommandTransaction;
    };


class SendPassword : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::SEND_PASSWORD, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendNewPassword : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::SEND_NEW_PASSWORD, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

// TODO fix original nomenclature
class SendSetReadonlyToUncryptedVolume : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendSetReadwriteToUncryptedVolume : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendClearNewSdCardFound : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::CLEAR_NEW_SD_CARD_FOUND, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendStartup : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint64_t localtime;  // POSIX
  };

  typedef Transaction<CommandID::SEND_STARTUP, struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class SendHiddenVolumeSetup : semantics::non_constructible {
 public:
  struct CommandPayload {
    // TODO HiddenVolumeSetup_tst type
  };

  typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class LockFirmware : semantics::non_constructible {
 public:
  struct CommandPayload {
    uint8_t password[30];
  };

  typedef Transaction<CommandID::SEND_LOCK_STICK_HARDWARE,
                      struct CommandPayload,
                      struct EmptyPayload> CommandTransaction;
};

class ProductionTest : semantics::non_constructible {
 public:
  typedef Transaction<CommandID::PRODUCTION_TEST, struct EmptyPayload,
                      struct EmptyPayload> CommandTransaction;
};
}
}
}

#endif