module Nats.Nkeys.Crc where

import Data.Bits (shiftR, shiftL, xor, (.&.))
import Data.Word (Word16, Word8)
import Data.ByteString

-- CRC-16 lookup table (CCITT XMODEM)
crcTable :: [Word16]
crcTable :: [Word16]
crcTable =
  [ Word16
0x0000,
    Word16
0x1021,
    Word16
0x2042,
    Word16
0x3063,
    Word16
0x4084,
    Word16
0x50a5,
    Word16
0x60c6,
    Word16
0x70e7,
    Word16
0x8108,
    Word16
0x9129,
    Word16
0xa14a,
    Word16
0xb16b,
    Word16
0xc18c,
    Word16
0xd1ad,
    Word16
0xe1ce,
    Word16
0xf1ef,
    Word16
0x1231,
    Word16
0x0210,
    Word16
0x3273,
    Word16
0x2252,
    Word16
0x52b5,
    Word16
0x4294,
    Word16
0x72f7,
    Word16
0x62d6,
    Word16
0x9339,
    Word16
0x8318,
    Word16
0xb37b,
    Word16
0xa35a,
    Word16
0xd3bd,
    Word16
0xc39c,
    Word16
0xf3ff,
    Word16
0xe3de,
    Word16
0x2462,
    Word16
0x3443,
    Word16
0x0420,
    Word16
0x1401,
    Word16
0x64e6,
    Word16
0x74c7,
    Word16
0x44a4,
    Word16
0x5485,
    Word16
0xa56a,
    Word16
0xb54b,
    Word16
0x8528,
    Word16
0x9509,
    Word16
0xe5ee,
    Word16
0xf5cf,
    Word16
0xc5ac,
    Word16
0xd58d,
    Word16
0x3653,
    Word16
0x2672,
    Word16
0x1611,
    Word16
0x0630,
    Word16
0x76d7,
    Word16
0x66f6,
    Word16
0x5695,
    Word16
0x46b4,
    Word16
0xb75b,
    Word16
0xa77a,
    Word16
0x9719,
    Word16
0x8738,
    Word16
0xf7df,
    Word16
0xe7fe,
    Word16
0xd79d,
    Word16
0xc7bc,
    Word16
0x48c4,
    Word16
0x58e5,
    Word16
0x6886,
    Word16
0x78a7,
    Word16
0x0840,
    Word16
0x1861,
    Word16
0x2802,
    Word16
0x3823,
    Word16
0xc9cc,
    Word16
0xd9ed,
    Word16
0xe98e,
    Word16
0xf9af,
    Word16
0x8948,
    Word16
0x9969,
    Word16
0xa90a,
    Word16
0xb92b,
    Word16
0x5af5,
    Word16
0x4ad4,
    Word16
0x7ab7,
    Word16
0x6a96,
    Word16
0x1a71,
    Word16
0x0a50,
    Word16
0x3a33,
    Word16
0x2a12,
    Word16
0xdbfd,
    Word16
0xcbdc,
    Word16
0xfbbf,
    Word16
0xeb9e,
    Word16
0x9b79,
    Word16
0x8b58,
    Word16
0xbb3b,
    Word16
0xab1a,
    Word16
0x6ca6,
    Word16
0x7c87,
    Word16
0x4ce4,
    Word16
0x5cc5,
    Word16
0x2c22,
    Word16
0x3c03,
    Word16
0x0c60,
    Word16
0x1c41,
    Word16
0xedae,
    Word16
0xfd8f,
    Word16
0xcdec,
    Word16
0xddcd,
    Word16
0xad2a,
    Word16
0xbd0b,
    Word16
0x8d68,
    Word16
0x9d49,
    Word16
0x7e97,
    Word16
0x6eb6,
    Word16
0x5ed5,
    Word16
0x4ef4,
    Word16
0x3e13,
    Word16
0x2e32,
    Word16
0x1e51,
    Word16
0x0e70,
    Word16
0xff9f,
    Word16
0xefbe,
    Word16
0xdfdd,
    Word16
0xcffc,
    Word16
0xbf1b,
    Word16
0xaf3a,
    Word16
0x9f59,
    Word16
0x8f78,
    Word16
0x9188,
    Word16
0x81a9,
    Word16
0xb1ca,
    Word16
0xa1eb,
    Word16
0xd10c,
    Word16
0xc12d,
    Word16
0xf14e,
    Word16
0xe16f,
    Word16
0x1080,
    Word16
0x00a1,
    Word16
0x30c2,
    Word16
0x20e3,
    Word16
0x5004,
    Word16
0x4025,
    Word16
0x7046,
    Word16
0x6067,
    Word16
0x83b9,
    Word16
0x9398,
    Word16
0xa3fb,
    Word16
0xb3da,
    Word16
0xc33d,
    Word16
0xd31c,
    Word16
0xe37f,
    Word16
0xf35e,
    Word16
0x02b1,
    Word16
0x1290,
    Word16
0x22f3,
    Word16
0x32d2,
    Word16
0x4235,
    Word16
0x5214,
    Word16
0x6277,
    Word16
0x7256,
    Word16
0xb5ea,
    Word16
0xa5cb,
    Word16
0x95a8,
    Word16
0x8589,
    Word16
0xf56e,
    Word16
0xe54f,
    Word16
0xd52c,
    Word16
0xc50d,
    Word16
0x34e2,
    Word16
0x24c3,
    Word16
0x14a0,
    Word16
0x0481,
    Word16
0x7466,
    Word16
0x6447,
    Word16
0x5424,
    Word16
0x4405,
    Word16
0xa7db,
    Word16
0xb7fa,
    Word16
0x8799,
    Word16
0x97b8,
    Word16
0xe75f,
    Word16
0xf77e,
    Word16
0xc71d,
    Word16
0xd73c,
    Word16
0x26d3,
    Word16
0x36f2,
    Word16
0x0691,
    Word16
0x16b0,
    Word16
0x6657,
    Word16
0x7676,
    Word16
0x4615,
    Word16
0x5634,
    Word16
0xd94c,
    Word16
0xc96d,
    Word16
0xf90e,
    Word16
0xe92f,
    Word16
0x99c8,
    Word16
0x89e9,
    Word16
0xb98a,
    Word16
0xa9ab,
    Word16
0x5844,
    Word16
0x4865,
    Word16
0x7806,
    Word16
0x6827,
    Word16
0x18c0,
    Word16
0x08e1,
    Word16
0x3882,
    Word16
0x28a3,
    Word16
0xcb7d,
    Word16
0xdb5c,
    Word16
0xeb3f,
    Word16
0xfb1e,
    Word16
0x8bf9,
    Word16
0x9bd8,
    Word16
0xabbb,
    Word16
0xbb9a,
    Word16
0x4a75,
    Word16
0x5a54,
    Word16
0x6a37,
    Word16
0x7a16,
    Word16
0x0af1,
    Word16
0x1ad0,
    Word16
0x2ab3,
    Word16
0x3a92,
    Word16
0xfd2e,
    Word16
0xed0f,
    Word16
0xdd6c,
    Word16
0xcd4d,
    Word16
0xbdaa,
    Word16
0xad8b,
    Word16
0x9de8,
    Word16
0x8dc9,
    Word16
0x7c26,
    Word16
0x6c07,
    Word16
0x5c64,
    Word16
0x4c45,
    Word16
0x3ca2,
    Word16
0x2c83,
    Word16
0x1ce0,
    Word16
0x0cc1,
    Word16
0xef1f,
    Word16
0xff3e,
    Word16
0xcf5d,
    Word16
0xdf7c,
    Word16
0xaf9b,
    Word16
0xbfba,
    Word16
0x8fd9,
    Word16
0x9ff8,
    Word16
0x6e17,
    Word16
0x7e36,
    Word16
0x4e55,
    Word16
0x5e74,
    Word16
0x2e93,
    Word16
0x3eb2,
    Word16
0x0ed1,
    Word16
0x1ef0
  ]

-- Calculate CRC-16 of a string
crc16 :: ByteString -> Word16
crc16 :: ByteString -> Word16
crc16 ByteString
str = (Word16 -> Word8 -> Word16) -> Word16 -> ByteString -> Word16
forall a. (a -> Word8 -> a) -> a -> ByteString -> a
foldl' Word16 -> Word8 -> Word16
crcUpdate Word16
0 ByteString
str

validate :: ByteString -> Word16 -> Bool
validate :: ByteString -> Word16 -> Bool
validate ByteString
input Word16
expected =
  ByteString -> Word16
crc16 ByteString
input Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
== Word16
expected
  
-- Update CRC-16 value
-- from the go code:
-- crc = ((crc << 8) & 0xffff) ^ crc16tab[((crc>>8)^uint16(b))&0x00FF]
crcUpdate :: Word16 -> Word8 -> Word16
crcUpdate :: Word16 -> Word8 -> Word16
crcUpdate Word16
crc Word8
byte = 
  let tableIndex :: Int
tableIndex = Word16 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (((Word16
crc Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftR` Int
8) Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
`xor` Word8 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
byte) Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0xff)
      crcVal :: Word16
crcVal = [Word16]
crcTable [Word16] -> Int -> Word16
forall a. HasCallStack => [a] -> Int -> a
!! Int
tableIndex Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
`xor` (Word16
crc Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftL` Int
8)
  in Word16
crcVal