/*
 * Copyright (c) 2015-2018 Nitrokey UG
 *
 * This file is part of libnitrokey.
 *
 * libnitrokey is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * libnitrokey is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: LGPL-3.0
 */

#ifndef STICK10_COMMANDS_H
#define STICK10_COMMANDS_H

#include <bitset>
#include <iomanip>
#include <string>
#include <sstream>
#include <stdint.h>
#include "device_proto.h"
#include "command.h"

#pragma pack (push,1)

namespace nitrokey {
namespace proto {



/*
 *	Stick10 protocol definition
 */
namespace stick10 {
class GetSlotName : public Command<CommandID::READ_SLOT_NAME> {
 public:
  // reachable as a typedef in Transaction
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return slot_number<0x10+3; }
      std::string dissect() const {
          std::stringstream ss;
          ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
          return ss.str();
      }
  } __packed;

  struct ResponsePayload {
    uint8_t slot_name[15];

    bool isValid() const { return true; }
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(slot_name);
        return ss.str();
      }
  } __packed;

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

class EraseSlot : Command<CommandID::ERASE_SLOT> {
 public:
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return !(slot_number & 0xF0); }
      std::string dissect() const {
          std::stringstream ss;
          ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
          return ss.str();
      }
  } __packed;

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

class SetTime : Command<CommandID::SET_TIME> {
 public:
  struct CommandPayload {
    uint8_t reset;  // 0 - get time, 1 - set time
    uint64_t time;  // posix time

    bool isValid() const { return reset && reset != 1; }
      std::string dissect() const {
          std::stringstream ss;
          ss << "reset:\t" << static_cast<int>(reset) << std::endl;
          ss << "time:\t" << (time) << std::endl;
          return ss.str();
      }
  } __packed;

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


class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
 public:
  struct CommandPayload {
    uint8_t slot_number;
    uint8_t slot_name[15];
    uint8_t slot_secret[20];
    union{
      uint8_t _slot_config;
        struct{
            bool use_8_digits   : 1;
            bool use_enter      : 1;
            bool use_tokenID    : 1;
        };
    };
      union{
        uint8_t slot_token_id[13]; /** OATH Token Identifier */
          struct{ /** @see https://openauthentication.org/token-specs/ */
              uint8_t omp[2];
              uint8_t tt[2];
              uint8_t mui[8];
              uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
          } slot_token_fields;
      };
      union{
        uint64_t slot_counter;
        uint8_t slot_counter_s[8];
      } __packed;

    bool isValid() const { return !(slot_number & 0xF0); }
    std::string dissect() const {
        std::stringstream ss;
        ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
        print_to_ss_volatile(slot_name);
        print_to_ss_volatile(slot_secret);
        ss << "slot_config:\t" << std::bitset<8>(static_cast<int>(_slot_config)) << std::endl;
        ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
        ss << "\tuse_enter(1):\t" << use_enter << std::endl;
        ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;

        ss << "slot_token_id:\t";
        for (auto i : slot_token_id)
            ss << std::hex << std::setw(2) << std::setfill('0')<< static_cast<int>(i) << " " ;
        ss << std::endl;
        ss << "slot_counter:\t[" << static_cast<int>(slot_counter) << "]\t"
         << ::nitrokey::misc::hexdump(reinterpret_cast<const uint8_t *>(&slot_counter), sizeof slot_counter, false);

      return ss.str();
    }
  } __packed;

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

class WriteToTOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
 public:
  struct CommandPayload {
    uint8_t slot_number;
    uint8_t slot_name[15];
    uint8_t slot_secret[20];
      union{
          uint8_t _slot_config;
          struct{
              bool use_8_digits   : 1;
              bool use_enter      : 1;
              bool use_tokenID    : 1;
          };
      };
      union{
          uint8_t slot_token_id[13]; /** OATH Token Identifier */
          struct{ /** @see https://openauthentication.org/token-specs/ */
              uint8_t omp[2];
              uint8_t tt[2];
              uint8_t mui[8];
              uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
          } slot_token_fields;
      };
    uint16_t slot_interval;

    bool isValid() const { return !(slot_number & 0xF0); } //TODO check
      std::string dissect() const {
          std::stringstream ss;
          ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
          print_to_ss_volatile(slot_name);
          print_to_ss_volatile(slot_secret);
          ss << "slot_config:\t" << std::bitset<8>(static_cast<int>(_slot_config)) << std::endl;
          ss << "slot_token_id:\t";
          for (auto i : slot_token_id)
              ss << std::hex << std::setw(2) << std::setfill('0')<< static_cast<int>(i) << " " ;
          ss << std::endl;
          ss << "slot_interval:\t" << static_cast<int>(slot_interval) << std::endl;
          return ss.str();
      }
  } __packed;

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

class GetTOTP : Command<CommandID::GET_CODE> {
 public:
  struct CommandPayload {
    uint8_t slot_number;
    uint64_t challenge;
    uint64_t last_totp_time;
    uint8_t last_interval;

    bool isValid() const { return !(slot_number & 0xF0); }
    std::string dissect() const {
      std::stringstream ss;
      ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
      ss << "challenge:\t" << (challenge) << std::endl;
      ss << "last_totp_time:\t" << (last_totp_time) << std::endl;
      ss << "last_interval:\t" << static_cast<int>(last_interval) << std::endl;
      return ss.str();
    }
  } __packed;

  struct ResponsePayload {
      union {
          uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
          struct {
              uint32_t code;
              union{
                  uint8_t _slot_config;
                  struct{
                      bool use_8_digits   : 1;
                      bool use_enter      : 1;
                      bool use_tokenID    : 1;
                  };
              };
          } __packed ;
      } __packed ;

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      ss << "code:\t" << (code) << std::endl;
        ss << "slot_config:\t" << std::bitset<8>(static_cast<int>(_slot_config)) << std::endl;
        ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
        ss << "\tuse_enter(1):\t" << use_enter << std::endl;
        ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
      return ss.str();
    }
  } __packed;

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

class GetHOTP : Command<CommandID::GET_CODE> {
 public:
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return (slot_number & 0xF0); }
    std::string dissect() const {
      std::stringstream ss;
      ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
      return ss.str();
    }
  } __packed;

  struct ResponsePayload {
      union {
          uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
          struct {
              uint32_t code;
              union{
                  uint8_t _slot_config;
                  struct{
                      bool use_8_digits   : 1;
                      bool use_enter      : 1;
                      bool use_tokenID    : 1;
                  };
              };
          } __packed;
      } __packed;

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      ss << "code:\t" << (code) << std::endl;
        ss << "slot_config:\t" << std::bitset<8>(static_cast<int>(_slot_config)) << std::endl;
        ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
        ss << "\tuse_enter(1):\t" << use_enter << std::endl;
        ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
      return ss.str();
    }
  } __packed;

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

class ReadSlot : Command<CommandID::READ_SLOT> {
 public:
  enum class CounterFormat {
    ASCII = 0,
    BINARY = 1,
  };
  struct CommandPayload {
    uint8_t slot_number;
    CounterFormat data_format; //Storage v0.54+ only: slot_counter value format: 0 - in ascii, 1 - binary

    bool isValid() const { return !(slot_number & 0xF0); }

    std::string dissect() const {
      std::stringstream ss;
      ss << "slot_number:\t" << static_cast<int>(slot_number) << std::endl;
      return ss.str();
    }
  } __packed;

  struct ResponsePayload {
    uint8_t slot_name[15];
    union{
      uint8_t _slot_config;
      struct{
        bool use_8_digits   : 1;
        bool use_enter      : 1;
        bool use_tokenID    : 1;
      };
    };
    union{
      uint8_t slot_token_id[13]; /** OATH Token Identifier */
      struct{ /** @see https://openauthentication.org/token-specs/ */
        uint8_t omp[2];
        uint8_t tt[2];
        uint8_t mui[8];
        uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
      } slot_token_fields;
    };
    union{
      uint64_t slot_counter;
      uint8_t slot_counter_s[8];
    } __packed;

    bool isValid() const { return true; }

    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(slot_name);
      ss << "slot_config:\t" << std::bitset<8>(static_cast<int>(_slot_config)) << std::endl;
      ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
      ss << "\tuse_enter(1):\t" << use_enter << std::endl;
      ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;

      ss << "slot_token_id:\t";
      for (auto i : slot_token_id)
        ss << std::hex << std::setw(2) << std::setfill('0')<< static_cast<int>(i) << " " ;
      ss << std::endl;
      ss << "slot_counter:\t[" << static_cast<int>(slot_counter) << "]\t"
         << ::nitrokey::misc::hexdump(reinterpret_cast<const uint8_t *>(&slot_counter), sizeof slot_counter, false);
      return ss.str();
    }
  } __packed;

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

class GetStatus : Command<CommandID::GET_STATUS> {
 public:
  struct ResponsePayload {
    union {
      uint16_t firmware_version;
      struct {
        uint8_t minor;
        uint8_t major;
      } firmware_version_st;
    };
    union{
      uint8_t card_serial[4];
      uint32_t card_serial_u32;
    } __packed;
      union {
          uint8_t general_config[5];
          struct{
              uint8_t numlock;     /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */
              uint8_t capslock;    /** same as numlock */
              uint8_t scrolllock;  /** same as numlock */
              uint8_t enable_user_password;
              uint8_t delete_user_password; /* unused */
          } __packed;
      } __packed;

    static constexpr uint8_t special_HOTP_slots = 2;
    bool isValid() const { return numlock < special_HOTP_slots && capslock < special_HOTP_slots
                                  && scrolllock < special_HOTP_slots && enable_user_password < 2; }

    std::string get_card_serial_hex() const {
      return nitrokey::misc::toHex(card_serial_u32);
    }

    std::string dissect() const {
      std::stringstream ss;
      ss  << "firmware_version:\t"
          << "[" << firmware_version << "]" << "\t"
          << ::nitrokey::misc::hexdump(
          reinterpret_cast<const uint8_t *>(&firmware_version), sizeof firmware_version, false);
      ss << "card_serial_u32:\t" << std::hex << card_serial_u32 << std::endl;
      ss << "card_serial:\t"
         << ::nitrokey::misc::hexdump(reinterpret_cast<const uint8_t *>(card_serial),
                                      sizeof card_serial, false);
      ss << "general_config:\t"
         << ::nitrokey::misc::hexdump(reinterpret_cast<const uint8_t *>(general_config),
                                      sizeof general_config, false);
        ss << "numlock:\t" << static_cast<int>(numlock) << std::endl;
        ss << "capslock:\t" << static_cast<int>(capslock) << std::endl;
        ss << "scrolllock:\t" << static_cast<int>(scrolllock) << std::endl;
        ss << "enable_user_password:\t" << static_cast<bool>(enable_user_password) << std::endl;
        ss << "delete_user_password:\t" << static_cast<bool>(delete_user_password) << std::endl;

        return ss.str();
    }
  } __packed;

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

class GetPasswordRetryCount : Command<CommandID::GET_PASSWORD_RETRY_COUNT> {
 public:
  struct ResponsePayload {
    uint8_t password_retry_count;

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      ss << " password_retry_count\t" << static_cast<int>(password_retry_count) << std::endl;
      return ss.str();
    }
  } __packed;

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

class GetUserPasswordRetryCount
    : Command<CommandID::GET_USER_PASSWORD_RETRY_COUNT> {
 public:
  struct ResponsePayload {
    uint8_t password_retry_count;

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      ss << " password_retry_count\t" << static_cast<int>(password_retry_count) << std::endl;
      return ss.str();
    }
  } __packed;

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

    template <typename T, typename Q, int N>
    void write_array(T &ss, Q (&arr)[N]){
        for (int i=0; i<N; i++){
            ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(arr[i]) << " ";
        }
        ss << std::endl;
    }


class GetPasswordSafeSlotStatus : Command<CommandID::GET_PW_SAFE_SLOT_STATUS> {
 public:
  struct ResponsePayload {
    uint8_t password_safe_status[PWS_SLOT_COUNT];

    bool isValid() const { return true; }
      std::string dissect() const {
          std::stringstream ss;
          ss << "password_safe_status\t";
          write_array(ss, password_safe_status);
          return ss.str();
      }
  } __packed;

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

class GetPasswordSafeSlotName : Command<CommandID::GET_PW_SAFE_SLOT_NAME> {
 public:
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return !(slot_number & 0xF0); }
    std::string dissect() const {
      std::stringstream ss;
      ss << "slot_number\t" << static_cast<int>(slot_number) << std::endl;
      return ss.str();
    }
  } __packed;

  struct ResponsePayload {
    uint8_t slot_name[PWS_SLOTNAME_LENGTH];

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(slot_name);
      return ss.str();
    }
  } __packed;

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

class GetPasswordSafeSlotPassword
    : Command<CommandID::GET_PW_SAFE_SLOT_PASSWORD> {
 public:
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return !(slot_number & 0xF0); }
    std::string dissect() const {
      std::stringstream ss;
      ss << "   slot_number\t" << static_cast<int>(slot_number) << std::endl;
      return ss.str();
    }
  } __packed;

  struct ResponsePayload {
    uint8_t slot_password[PWS_PASSWORD_LENGTH];

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(slot_password);
      return ss.str();
    }
  } __packed;

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

class GetPasswordSafeSlotLogin
    : Command<CommandID::GET_PW_SAFE_SLOT_LOGINNAME> {
 public:
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return !(slot_number & 0xF0); }
    std::string dissect() const {
      std::stringstream ss;
      ss << "   slot_number\t" << static_cast<int>(slot_number) << std::endl;
      return ss.str();
    }
  } __packed;

  struct ResponsePayload {
    uint8_t slot_login[PWS_LOGINNAME_LENGTH];

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(slot_login);
      return ss.str();
    }
  } __packed;

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

class SetPasswordSafeSlotData : Command<CommandID::SET_PW_SAFE_SLOT_DATA_1> {
 public:
  struct CommandPayload {
    uint8_t slot_number;
    uint8_t slot_name[PWS_SLOTNAME_LENGTH];
    uint8_t slot_password[PWS_PASSWORD_LENGTH];

    bool isValid() const { return !(slot_number & 0xF0); }
      std::string dissect() const {
          std::stringstream ss;
          ss << " slot_number\t" << static_cast<int>(slot_number) << std::endl;
          print_to_ss_volatile(slot_name);
          print_to_ss_volatile(slot_password);
          return ss.str();
      }
  } __packed;

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

class SetPasswordSafeSlotData2 : Command<CommandID::SET_PW_SAFE_SLOT_DATA_2> {
 public:
  struct CommandPayload {
    uint8_t slot_number;
    uint8_t slot_login_name[PWS_LOGINNAME_LENGTH];

    bool isValid() const { return !(slot_number & 0xF0); }
      std::string dissect() const {
        std::stringstream ss;
        ss << " slot_number\t" << static_cast<int>(slot_number) << std::endl;
        print_to_ss_volatile(slot_login_name);
        return ss.str();
      }
  } __packed;

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

class ErasePasswordSafeSlot : Command<CommandID::PW_SAFE_ERASE_SLOT> {
 public:
  struct CommandPayload {
    uint8_t slot_number;

    bool isValid() const { return !(slot_number & 0xF0); }
      std::string dissect() const {
          std::stringstream ss;
          ss << " slot_number\t" << static_cast<int>(slot_number) << std::endl;
          return ss.str();
      }

  } __packed;

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

class EnablePasswordSafe : Command<CommandID::PW_SAFE_ENABLE> {
 public:
  struct CommandPayload {
    uint8_t user_password[30];

    bool isValid() const { return true; }
    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(user_password);
      return ss.str();
    }
  } __packed;

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

class PasswordSafeInitKey : Command<CommandID::PW_SAFE_INIT_KEY> {
    /**
     * never used in Nitrokey App
     */
 public:
  typedef Transaction<command_id(), struct EmptyPayload, struct EmptyPayload>
      CommandTransaction;
};

class PasswordSafeSendSlotViaHID : Command<CommandID::PW_SAFE_SEND_DATA> {
    /**
     * never used in Nitrokey App
     */
 public:
  struct CommandPayload {
    uint8_t slot_number;
    uint8_t slot_kind;

    bool isValid() const { return !(slot_number & 0xF0); }
  } __packed;

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

// TODO "Device::passwordSafeSendSlotDataViaHID"

class WriteGeneralConfig : Command<CommandID::WRITE_CONFIG> {
 public:
  struct CommandPayload {
    union{
        uint8_t config[5];
        struct{
            uint8_t numlock;     /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */
            uint8_t capslock;    /** same as numlock */
            uint8_t scrolllock;  /** same as numlock */
            uint8_t enable_user_password;
            uint8_t delete_user_password;
        };
    };
    bool isValid() const { return numlock < 2 && capslock < 2 && scrolllock < 2 && enable_user_password < 2; }

    std::string dissect() const {
          std::stringstream ss;
          ss << "numlock:\t" << static_cast<int>(numlock) << std::endl;
          ss << "capslock:\t" << static_cast<int>(capslock) << std::endl;
          ss << "scrolllock:\t" << static_cast<int>(scrolllock) << std::endl;
          ss << "enable_user_password:\t" << static_cast<bool>(enable_user_password) << std::endl;
          ss << "delete_user_password:\t" << static_cast<bool>(delete_user_password) << std::endl;
          return ss.str();
      }
  } __packed;

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

class FirstAuthenticate : Command<CommandID::FIRST_AUTHENTICATE> {
 public:
  struct CommandPayload {
    uint8_t card_password[25];
    uint8_t temporary_password[25];

    bool isValid() const { return true; }

    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(card_password);
      hexdump_to_ss(temporary_password);
      return ss.str();
    }
  } __packed;

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

class UserAuthenticate : Command<CommandID::USER_AUTHENTICATE> {
 public:
  struct CommandPayload {
    uint8_t card_password[25];
    uint8_t temporary_password[25];

    bool isValid() const { return true; }
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(card_password);
        hexdump_to_ss(temporary_password);
        return ss.str();
      }
  } __packed;

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

class Authorize : Command<CommandID::AUTHORIZE> {
 public:
  struct CommandPayload {
    uint32_t crc_to_authorize;
    uint8_t temporary_password[25];

      std::string dissect() const {
      std::stringstream ss;
      ss << " crc_to_authorize:\t" << std::hex << std::setw(2) << std::setfill('0') << crc_to_authorize<< std::endl;
      hexdump_to_ss(temporary_password);
      return ss.str();
    }
  } __packed;

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

class UserAuthorize : Command<CommandID::USER_AUTHORIZE> {
 public:
  struct CommandPayload {
    uint32_t crc_to_authorize;
    uint8_t temporary_password[25];
    std::string dissect() const {
      std::stringstream ss;
      ss << " crc_to_authorize:\t" <<  crc_to_authorize<< std::endl;
      hexdump_to_ss(temporary_password);
      return ss.str();
    }
  } __packed;

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

class UnlockUserPassword : Command<CommandID::UNLOCK_USER_PASSWORD> {
 public:
  struct CommandPayload {
    uint8_t admin_password[25];
    uint8_t user_new_password[25];
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(admin_password);
        print_to_ss_volatile(user_new_password);
        return ss.str();
      }
  } __packed;

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

class ChangeUserPin : Command<CommandID::CHANGE_USER_PIN> {
 public:
  struct CommandPayload {
    uint8_t old_pin[25];
    uint8_t new_pin[25];
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(old_pin);
        print_to_ss_volatile(new_pin);
        return ss.str();
      }
  } __packed;

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

class IsAESSupported : Command<CommandID::DETECT_SC_AES> {
 public:
  struct CommandPayload {
    uint8_t user_password[20];
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(user_password);
        return ss.str();
      }
  } __packed;

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


class ChangeAdminPin : Command<CommandID::CHANGE_ADMIN_PIN> {
 public:
  struct CommandPayload {
    uint8_t old_pin[25];
    uint8_t new_pin[25];
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(old_pin);
        print_to_ss_volatile(new_pin);
        return ss.str();
      }
  } __packed;

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

class LockDevice : Command<CommandID::LOCK_DEVICE> {
 public:
  typedef Transaction<command_id(), struct EmptyPayload, struct EmptyPayload>
      CommandTransaction;
};

class FactoryReset : Command<CommandID::FACTORY_RESET> {
 public:
  struct CommandPayload {
    uint8_t admin_password[20];
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(admin_password);
        return ss.str();
      }
  } __packed;

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

class BuildAESKey : Command<CommandID::NEW_AES_KEY> {
 public:
  struct CommandPayload {
    uint8_t admin_password[20];
      std::string dissect() const {
        std::stringstream ss;
        print_to_ss_volatile(admin_password);
        return ss.str();
      }
  } __packed;

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

};

class GetRandom : Command<CommandID::GET_RANDOM> {
 public:
    static const int DATA_SIZE_MAX = 64 - 13;
    struct CommandPayload {
        uint8_t size_requested;

        bool isValid() const { return size_requested < DATA_SIZE_MAX; }
        std::string dissect() const {
            std::stringstream ss;
            print_to_ss_int(size_requested);
            return ss.str();
        }
    } __packed;

    struct ResponsePayload {
        uint8_t op_success;
        uint8_t size_effective;
        uint8_t data[DATA_SIZE_MAX];

        bool isValid() const { return true; }
        std::string dissect() const {
            std::stringstream ss;
            print_to_ss_int(op_success);
            print_to_ss_int(size_effective);
            hexdump_to_ss(data);
            return ss.str();
        }
    } __packed;


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

};

class FirmwareUpdate : Command<CommandID::FIRMWARE_UPDATE> {
public:
  struct CommandPayload {
    uint8_t firmware_password[20];
    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(firmware_password);
      return ss.str();
      }
  } __packed;

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

};

class FirmwarePasswordChange : Command<CommandID::FIRMWARE_PASSWORD_CHANGE> {
public:
  struct CommandPayload {
    uint8_t firmware_password_current[20];
    uint8_t firmware_password_new[20];
    std::string dissect() const {
      std::stringstream ss;
      print_to_ss_volatile(firmware_password_current);
      print_to_ss_volatile(firmware_password_new);
      return ss.str();
    }
  } __packed;

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

};


}
}
}
#pragma pack (pop)
#endif
