// ///////////////////////////////////////////////////////////////////////////////
// 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 "MKFakeConnection.h"
#import "NSData+MKCommandDecode.h"
#import "NSData+MKCommandEncode.h"
#import "NSData+MKPayloadDecode.h"
#import "NSData+MKPayloadEncode.h"

#import "MKDataConstants.h"

static NSString * const MKDummyConnectionException = @"MKDummyConnectionException";

@interface MKFakeConnection (Private)
 
- (void) doConnect;
- (void) doDisconnect;
- (void) doResponseMkData:(NSData*)data;

@end

@implementation MKFakeConnection

#pragma mark Properties

@synthesize delegate;

#pragma mark Initialization

- (id) init {
  return [self initWithDelegate:nil];
}

- (id) initWithDelegate:(id)theDelegate;
{
  if (self = [super init]) {
    self.delegate = theDelegate;
    
    NSString *path = [[NSBundle mainBundle] pathForResource:@"AllSettings" 
                                                     ofType:@"plist"];
    
    settings = [[NSMutableArray arrayWithContentsOfFile:path] retain];
    
    activeSetting = 3;
  }
  return self;
}

- (void) dealloc {
  [settings release];
  [super dealloc];
}

#pragma mark -
#pragma mark MKInput

- (BOOL) connectTo:(NSString *)hostOrDevice error:(NSError **)err;
{
  if (delegate == nil) {
    [NSException raise:MKDummyConnectionException
                format:@"Attempting to connect without a delegate. Set a delegate first."];
  }

  NSArray * hostItems = [hostOrDevice componentsSeparatedByString:@":"];
  if ( [hostItems count] != 2 ) {
    [NSException raise:MKDummyConnectionException
                format:@"Attempting to connect without a port. Set a port first."];

  }

  int port = [[hostItems objectAtIndex:1] intValue];
  NSString * host = [hostItems objectAtIndex:0];

  DLog(@"Try to connect to %@ on port %d", host, port);

  [self performSelector:@selector(doConnect) withObject:nil afterDelay:0.5];
  return YES;
}

- (BOOL) isConnected;
{
  return isConnected;
}

- (void) disconnect;
{
  [self performSelector:@selector(doDisconnect) withObject:nil afterDelay:0.1];
}

- (void) writeMkData:(NSData *)data;
{
  [self performSelector:@selector(doResponseMkData:) withObject:[data retain] afterDelay:0.1];
}

#pragma mark -


- (NSData*) versionResponse;
{
  VersionInfo v;
  v.SWMajor = 0;
  v.SWMinor = 78;
  v.ProtoMajor = 3; 
  v.ProtoMinor = 1;
  v.SWPatch = 3;
  NSData * payload = [NSData dataWithBytes:(void*)&v length:sizeof(v)];
  return payload;
}

- (NSData*) debugResponse;
{
  DebugOut d;
  NSData * payload = [NSData dataWithBytes:(void*)&d length:sizeof(d)];
  return payload;
}

- (NSData*) channelResponse;
{
  int16_t data[26];
  
  for (int i=0; i<26; i++) {
    data[i]=random()%250;
  }
  
  NSData * payload = [NSData dataWithBytes:(void*)&data length:sizeof(data)];
  return payload;
}



- (NSData*) menuResponse:(NSData*)payload {

  const char * bytes = [payload bytes];
  
  uint8_t key=(uint8_t)bytes[0];
//  uint8_t interval=(uint8_t)bytes[1];
  
  if (key==0xFD) {
    menuPage++;
  } else if (key==0xFE) {
    menuPage--;
  }
  
  menuPage %= 16;
  menuCounter = 1;
  
  NSString* screen=[NSString stringWithFormat:@"Page %02d (%d)---------12345678901234567890abcdefghijklmnopqrst++++++++++++++++++++",
                    menuPage,menuCounter];

  NSData * newPayload = [screen dataUsingEncoding:NSASCIIStringEncoding];

  [self performSelector:@selector(resendMenuResponse) withObject:nil afterDelay:0.5];
 
  return newPayload;
}

- (void) resendMenuResponse {

  NSString* screen=[NSString stringWithFormat:@"Page %02d (%d)---------12345678901234567890abcdefghijklmnopqrst++++++++++++++++++++",
                    menuPage,menuCounter];
  
  NSData * newPayload = [screen dataUsingEncoding:NSASCIIStringEncoding];
  
  NSData * rspData = [newPayload dataWithCommand:MKCommandLcdResponse forAddress:MKAddressFC];
  
  if ( [delegate respondsToSelector:@selector(didReadMkData:)] ) {
    [delegate didReadMkData:rspData];
  } 
  
  if ( (++menuCounter)<2 ) {
    [self performSelector:@selector(resendMenuResponse) withObject:nil afterDelay:0.5];
  }
} 



- (NSData*) changeSettingsResponse:(NSData*)payload {
  
  const char * bytes = [payload bytes];
  
  uint8_t index=(uint8_t)bytes[0];
  
  activeSetting = index;
  
  NSData * newPayload = [NSData dataWithBytes:(void*)&index length:sizeof(index)];
  return newPayload;
  
}

- (NSData*) writeSettingResponse:(NSData*)payload {

  NSDictionary* d = [payload decodeReadSettingResponse];
  
  NSNumber* theIndex = [d objectForKey:kMKDataKeyIndex]; 
  uint8_t index = [theIndex unsignedCharValue];
  
  [settings replaceObjectAtIndex:index-1 withObject:d];
  
  NSData * newPayload = [NSData dataWithBytes:(void*)&index length:sizeof(index)];
  return newPayload;
}

- (NSData*) readSettingResponse:(NSData*)payload {
  
  const char * bytes = [payload bytes];
  
  uint8_t index=(uint8_t)bytes[0];
  
  if (index==0xFF) {
    index=activeSetting;
  }
  
  index--;
  
  NSDictionary* d = [settings objectAtIndex:index];
  
  DLog(@"%@",d);
    
  NSData * newPayload = [NSData payloadForWriteSettingRequest:d];
  
  return newPayload;
}

#pragma mark -

- (void) doConnect {

  isConnected=YES;
  if ( [delegate respondsToSelector:@selector(didConnectTo:)] ) {
    [delegate didConnectTo:@"Dummy"];
  }

  NSData * data = [NSData dataWithCommand:MKCommandVersionRequest
                               forAddress:MKAddressAll
                         payloadWithBytes:NULL
                                   length:0];

  [self writeMkData:data];
}


- (void) doDisconnect {
  isConnected=NO;
  if ( [delegate respondsToSelector:@selector(didDisconnect)] ) {
    [delegate didDisconnect];
  }
}

- (void) doResponseMkData:(NSData*)data {

  if ([data isCrcOk]) {

    NSData * payload = [data payload];
//    MKAddress address = [data address];

    NSData * rspPayload;
    MKCommandId rspCommand;  
    
    DLog(@"Need responde for command %c",[data command]);

    switch ([data command]) {
/*
      case MKCommandLcdMenuResponse:
        n = MKLcdMenuNotification;
        d = [payload decodeLcdMenuResponse];
        break;
      case MKCommandLcdResponse:
        n = MKLcdNotification;
        d = [payload decodeLcdResponse];
        break;
      case MKCommandDebugLabelResponse:
        n = MKDebugLabelNotification;
        d = [payload decodeAnalogLabelResponse];
        break;
*/
      case MKCommandLcdRequest:
        rspPayload = [self menuResponse:payload];
        rspCommand = MKCommandLcdResponse;
        break;
      case MKCommandDebugValueRequest:
        rspPayload = [self debugResponse];
        rspCommand = MKCommandDebugValueResponse;
        break;
      case MKCommandChannelsValueRequest:
        rspPayload = [self channelResponse];
        rspCommand = MKCommandChannelsValueResponse;
        break;
      case MKCommandVersionRequest:
        rspPayload = [self versionResponse];
        rspCommand = MKCommandVersionResponse;
        break;
      case MKCommandChangeSettingsRequest:
        rspPayload = [self changeSettingsResponse:payload];
        rspCommand = MKCommandChangeSettingsResponse;
        break;
      case MKCommandReadSettingsRequest:
        rspPayload = [self readSettingResponse:payload];
        rspCommand = MKCommandReadSettingsResponse;
        break;
      case MKCommandWriteSettingsRequest:
        rspPayload = [self writeSettingResponse:payload];
        rspCommand = MKCommandWriteSettingsResponse;
        break;
      case MKCommandEngineTestRequest:
        DLog(@"Engine Test %@",payload);
        [data release];
        return;
      default:
        ALog(@"Unknown command %c",[data command]);
        [data release];
        return;
    }

    NSData * rspData = [rspPayload dataWithCommand:rspCommand forAddress:MKAddressFC];
    
    if ( [delegate respondsToSelector:@selector(didReadMkData:)] ) {
      [delegate didReadMkData:rspData];
    } 
  
  }  

  [data release];
}

@end
