/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import "MIDIMessage.h"

const uint8_t kMIDINoChannel = -1;

MIDIMessageType MIDIMessageTypeFromStatus(MIDIByte status) {
  if (status < MIDIMessageSysEx) {
    return (status & 0xF0) >> 4;
  } else {
    return status;
  }
}

MIDIChannel MIDIChannelFromStatus(MIDIByte status) {
  if (status < MIDIMessageSysEx) {
    return (status & 0x0F) + 1;
  } else {
    return -1;
  }
}

NSData *MIDIMessageBody(NSData *message) {
  if (message.length == 0) {
    return nil;
  }
  
  const MIDIByte *bytes = (const MIDIByte *)message.bytes;
  
  // Slice off any header/trailer bytes.
  if (MIDIMessageTypeFromStatus(bytes[0]) == MIDIMessageSysEx) {
    NSCAssert(bytes[message.length - 1] == MIDIMessageSysExEnd, @"SysEx message without trailer.");
    return [message subdataWithRange:NSMakeRange(1, message.length - 2)];
  } else {
    return [message subdataWithRange:NSMakeRange(1, message.length - 1)];
  }
}

MIDIByte MIDIStatusByte(MIDIMessageType type, MIDIChannel channel) {
  if (type >= MIDIMessageSysEx) {
    return type;
  } else {
    return (type << 4) | (channel - 1);
  }
}

NSData *MIDIChannelMessageCreate(MIDIMessageType type, MIDIChannel channel, NSData *body) {
  NSMutableData *message =
      [[NSMutableData alloc] initWithCapacity:body.length + 2];  // +2 for status and SysEx trailer
  
  const MIDIByte status = MIDIStatusByte(type, channel);
  [message appendBytes:&status length:1];
  [message appendData:body];

  if (type == MIDIMessageSysEx) {
    const MIDIByte trailer = MIDIMessageSysEx;
    [message appendBytes:&trailer length:1];
  }
  
  return message;
}

NSData *MIDIMessageCreateSimple1(MIDIMessageType type, MIDIChannel channel, MIDIByte first) {
  NSCAssert(type != MIDIMessageSysEx, @"MIDIMessageCreateSimple1 cannot create SysEx messages.");

  NSMutableData *message = [[NSMutableData alloc] initWithCapacity:2];  // Status + Data
  
  const MIDIByte status = MIDIStatusByte(type, channel);
  [message appendBytes:&status length:1];
  [message appendBytes:&first length:1];
  
  return message;
}

NSData *MIDIMessageCreateSimple2(MIDIMessageType type,
                                 MIDIChannel channel,
                                 MIDIByte first,
                                 MIDIByte second) {
  NSCAssert(type != MIDIMessageSysEx, @"MIDIMessageCreateSimple2 cannot create SysEx messages.");
  
  NSMutableData *message = [[NSMutableData alloc] initWithCapacity:3];  // Status + Data + Data
  
  const MIDIByte status = MIDIStatusByte(type, channel);
  [message appendBytes:&status length:1];
  [message appendBytes:&first length:1];
  [message appendBytes:&second length:1];
  
  return message;
}