// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////

#import "NSData+MKCommandDecode.h"

#define kInvalidMKCommand @"Invalid MK command"

// ///////////////////////////////////////////////////////////////////////////////
#pragma mark Helper funktions

static NSData * decode64(const char * inBuffer, int length)
{
  unsigned char a, b, c, d;
  unsigned char x, y, z;
  
  int srcIdx = 0;
  int dstIdx = 0;
  
  NSMutableData * outData = [NSMutableData dataWithLength:length];
  unsigned char * outBuffer = [outData mutableBytes];
  
  if (inBuffer[srcIdx] != 0) 
  {
    while (length != 0) 
    {
      a = inBuffer[srcIdx++] - '=';
      b = inBuffer[srcIdx++] - '=';
      c = inBuffer[srcIdx++] - '=';
      d = inBuffer[srcIdx++] - '=';
      
      x = (a << 2) | (b >> 4);
      y = ((b & 0x0f) << 4) | (c >> 2);
      z = ((c & 0x03) << 6) | d;
      
      if (length--) outBuffer[dstIdx++] = x; else break;
      if (length--) outBuffer[dstIdx++] = y; else break;
      if (length--) outBuffer[dstIdx++] = z; else break;
    }
  }
  
  [outData setLength:dstIdx];
  return outData;
}

// ///////////////////////////////////////////////////////////////////////////////
@implementation NSData (MKCommandDecode)

- (NSUInteger) frameLength;
{
  NSUInteger frameLength =  [self length];
  const char * frameBytes = [self bytes];
  
  if (frameLength > 0 && frameBytes[frameLength - 1] == '\r')
    frameLength--;
  
  return frameLength;
}


- (BOOL) isMkData 
{
  NSUInteger frameLength =  [self frameLength];
  const char * frameBytes = [self bytes];
  
  if ( frameLength < 5) 
  {
    NSLog(@"The frame length is to short %d. Frame is invalid", frameLength);
    return NO;
  }
  
  if (frameBytes[0] != '#') 
  {
    NSLog(@"The frame is no MK frame");
    return NO;
  }
  
  return YES;
}


- (BOOL) isCrcOk 
{
  
  if (![self isMkData])
    return NO;
  
  NSUInteger frameLength =  [self frameLength];
  const uint8_t * frameBytes = [self bytes];
  
  uint8_t crc2 = frameBytes[frameLength - 1];
  uint8_t crc1 = frameBytes[frameLength - 2];
  
  int crc = 0;
  for (int i = 0; i < frameLength - 2; i++) 
  {
    crc += frameBytes[i];
  }
  
  crc %= 4096;
  
  if (crc1 == ('=' + (crc / 64)) && crc2 == ('=' + crc % 64))
    return YES;
  
  return NO;
}

- (MKAddress) address 
{
  if (![self isCrcOk])
    [NSException raise:kInvalidMKCommand format:@"cannot get the address from a invalid MK frame"];
  
  const char * frameBytes = [self bytes];
  
  return (MKAddress)(frameBytes[1] - 'a');
}

- (MKCommandId) command 
{
  if (![self isCrcOk])
    [NSException raise:kInvalidMKCommand format:@"cannot get the address from a invalid MK frame"];
  
  const char * frameBytes = [self bytes];
  return frameBytes[2];
}


- (NSData *) payload;
{
  NSUInteger frameLength =  [self frameLength];
  const char * frameBytes = [self bytes];
  
  int startIndex = 3;
  int frameDataLength = frameLength - startIndex - 2;
  
  return decode64(frameBytes + startIndex, frameDataLength);
}


@end
