package apdu

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
)

const (
	// SwOK is returned from smartcards as a positive response code.
	SwOK = 0x9000
)

// ErrBadResponse defines an error conaining the returned Sw code and a description message.
type ErrBadResponse struct {
	Sw      uint16
	message string
}

// NewErrBadResponse returns a ErrBadResponse with the specified sw and message values.
func NewErrBadResponse(sw uint16, message string) *ErrBadResponse {
	return &ErrBadResponse{
		Sw:      sw,
		message: message,
	}
}

// Error implements the error interface.
func (e *ErrBadResponse) Error() string {
	return fmt.Sprintf("bad response %x: %s", e.Sw, e.message)
}

// Response represents a struct containing the smartcard response fields.
type Response struct {
	Data []byte
	Sw1  uint8
	Sw2  uint8
	Sw   uint16
}

// ErrBadRawResponse is an error returned by ParseResponse in case the response data is not long enough.
var ErrBadRawResponse = errors.New("response data must be at least 2 bytes")

// ParseResponse parses a raw response and return a Response.
func ParseResponse(data []byte) (*Response, error) {
	r := &Response{}
	return r, r.deserialize(data)
}

func (r *Response) deserialize(data []byte) error {
	if len(data) < 2 {
		return ErrBadRawResponse
	}

	r.Data = make([]byte, len(data)-2)
	buf := bytes.NewReader(data)

	if err := binary.Read(buf, binary.BigEndian, &r.Data); err != nil {
		return err
	}

	if err := binary.Read(buf, binary.BigEndian, &r.Sw1); err != nil {
		return err
	}

	if err := binary.Read(buf, binary.BigEndian, &r.Sw2); err != nil {
		return err
	}

	r.Sw = (uint16(r.Sw1) << 8) | uint16(r.Sw2)

	return nil
}

// IsOK returns true if the response Sw code is 0x9000.
func (r *Response) IsOK() bool {
	return r.Sw == SwOK
}
