# frozen_string_literal: true

module Libptouch
  # 32 バイトのステータス応答を Hash に展開する（C の libptouch_status_fprint と同じ区分）。
  module StatusHash
    class << self
      def decode(raw)
        unless raw.bytesize == STATUS_LENGTH
          raise ArgumentError,
                "expected #{STATUS_LENGTH} bytes, got #{raw.bytesize}"
        end

        s = raw.bytes

        {
          header_ok: s[0] == 0x80 && s[1] == 0x20,
          header: [s[0], s[1]],
          brother_code: s[2],
          brother_code_char: s[2] >= 32 && s[2] < 127 ? s[2].chr(Encoding::ASCII_8BIT) : nil,
          model: model_entry(s[4]),
          region_code: s[5],
          region_char: s[5] >= 32 && s[5] < 127 ? s[5].chr(Encoding::ASCII_8BIT) : nil,
          battery: labeled(BATTERY, s[6]),
          extended_error: extended_error_entry(s[7]),
          error_info1: error_info1(s[8]),
          error_info2: error_info2(s[9]),
          media_width: media_width_entry(s[10], s[17]),
          tape_kind: labeled(MEDIA_KIND, s[11]),
          color_count: s[12],
          font_jp: s[13],
          font: s[14],
          mode: s[15],
          density: s[16],
          status_kind: labeled(STATUS_KIND, s[18]),
          phase_type: s[19],
          phase_number: [s[20], s[21]],
          notification_number: s[22],
          extended_section_bytes: s[23],
          tape_color: labeled(TAPE_COLOR, s[24]),
          text_color: s[25],
          raw_hex: raw.unpack1("H*"),
          raw_bytes: s.dup
        }
      end

      private

      def model_entry(code)
        {
          code: code,
          name: MODEL_NAMES[code],
          ascii_char: code >= 32 && code < 127 ? code.chr(Encoding::ASCII_8BIT) : nil
        }
      end

      def labeled(table, byte)
        {
          code: byte,
          label: table[byte]
        }
      end

      def extended_error_entry(byte)
        h = { code: byte, label: EXTENDED_ERROR[byte] }
        h[:none] = true if byte.zero?
        h
      end

      def media_width_entry(w, len_byte)
        entry = labeled(MEDIA_WIDTH, w)
        if w == 0x15 && len_byte != 0
          entry[:media_length_code] = len_byte
          entry[:media_length_note_mm] = len_byte
        end
        entry
      end

      def error_info1(b)
        {
          raw: b,
          media_missing: !!(b & 0x01),
          media_end: !!(b & 0x02),
          cutter_jam: !!(b & 0x04),
          battery_weak: !!(b & 0x08),
          high_voltage_adapter: !!(b & 0x40)
        }
      end

      def error_info2(b)
        {
          raw: b,
          media_mismatch: !!(b & 0x01),
          comm_error: !!(b & 0x04),
          comm_buffer_full: !!(b & 0x08),
          cover_open: !!(b & 0x10),
          heat_error: !!(b & 0x20),
          tip_detection_error: !!(b & 0x40),
          system_error: !!(b & 0x80)
        }
      end
    end

    MODEL_NAMES = {
      0x6F => "PT-P900W",
      0x70 => "PT-P950NW",
      0x71 => "PT-P900",
      0x78 => "PT-P910BT"
    }.freeze

    MEDIA_WIDTH = {
      0x00 => "テープなし / 未装着",
      0x04 => "3.5 mm",
      0x06 => "6 mm",
      0x09 => "9 mm",
      0x0C => "12 mm",
      0x12 => "18 mm",
      0x18 => "24 mm",
      0x24 => "36 mm",
      0x15 => "FLe 21 mm 幅（長さはメディア長バイト参照）"
    }.freeze

    MEDIA_KIND = {
      0x00 => "テープなし",
      0x01 => "ラミネートテープ",
      0x03 => "ノンラミネートテープ",
      0x04 => "ファブリックテープ",
      0x11 => "ヒートシュリンクチューブ (HS 2:1)",
      0x13 => "FLe テープ",
      0x14 => "フレキシブルＩＤテープ",
      0x15 => "サテンテープ",
      0x17 => "ヒートシュリンクチューブ (HS 3:1)",
      0xFF => "非対応テープ"
    }.freeze

    BATTERY = {
      0x00 => "フル",
      0x01 => "ハーフ",
      0x02 => "ロー",
      0x03 => "要充電",
      0x04 => "AC アダプター使用中",
      0xFF => "不明"
    }.freeze

    TAPE_COLOR = {
      0x01 => "白 (White)",
      0x02 => "その他 (Other)",
      0x03 => "透明 (Clear)",
      0x04 => "赤 (Red)",
      0x05 => "青 (Blue)",
      0x06 => "黄 (Yellow)",
      0x07 => "緑 (Green)",
      0x08 => "黒 (Black)",
      0x09 => "透明（文字白）",
      0x20 => "白（マット） (Matte White)",
      0x21 => "透明（マット） (Matte Clear)",
      0x22 => "銀（マット） (Matte Silver)",
      0x23 => "金（サテン） (Satin Gold)",
      0x24 => "銀（サテン） (Satin Silver)",
      0x30 => "青（D）",
      0x31 => "赤（D）",
      0x40 => "オレンジ（蛍光）",
      0x41 => "黄（蛍光）",
      0x50 => "ピンク（S）",
      0x51 => "グレー（S）",
      0x52 => "グリーン（S）",
      0x60 => "イエロー（F）",
      0x61 => "ピンク（F）",
      0x62 => "ブルー（F）",
      0x70 => "白（チューブ）",
      0x90 => "白（フレキ）",
      0x91 => "黄（フレキ）",
      0xF0 => "クリーニング",
      0xF1 => "ステンシル",
      0xFF => "非対応"
    }.freeze

    STATUS_KIND = {
      0x00 => "印刷終了",
      0x01 => "エラー発生",
      0x02 => "IF モード終了",
      0x03 => "パワーオフ（未使用扱い）",
      0x04 => "通知",
      0x05 => "フェーズ変更"
    }.freeze

    EXTENDED_ERROR = {
      0x10 => "FLE のテープエンド",
      0x1D => "高解像度/ドラフト印刷エラー",
      0x1E => "アダプター抜き挿しエラー",
      0x21 => "非対応メディアエラー"
    }.freeze
  end

  def self.parse_status(raw)
    StatusHash.decode(raw)
  end
end
