// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// TODO(schwehr): Add logging back in or remove it.

#include "decode_body.h"

#include <memory>

#include "ais.h"

using libais::AisMsg;
using std::unique_ptr;

namespace libais {

// TODO(schwehr): Switch to make_unique when C++14 is available on Travis-CI.
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args &&... args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

unique_ptr<AisMsg> CreateAisMsg6(const string &body, const int fill_bits) {
  libais::Ais6 msg(body.c_str(), fill_bits);
  switch (msg.dac) {
    // International Maritime Organization (IMO).
    case libais::AIS_DAC_1_INTERNATIONAL:
      switch (msg.fi) {
        case 0:
          return MakeUnique<libais::Ais6_1_0>(body.c_str(), fill_bits);
        case 1:
          return MakeUnique<libais::Ais6_1_1>(body.c_str(), fill_bits);
        case 2:
          return MakeUnique<libais::Ais6_1_2>(body.c_str(), fill_bits);
        case 3:
          return MakeUnique<libais::Ais6_1_3>(body.c_str(), fill_bits);
        case 4:
          return MakeUnique<libais::Ais6_1_4>(body.c_str(), fill_bits);
        case 12:
          return MakeUnique<libais::Ais6_1_12>(body.c_str(), fill_bits);
        case 14:
          return MakeUnique<libais::Ais6_1_14>(body.c_str(), fill_bits);
        case 18:
          return MakeUnique<libais::Ais6_1_18>(body.c_str(), fill_bits);
        case 20:
          return MakeUnique<libais::Ais6_1_20>(body.c_str(), fill_bits);
        case 25:
          return MakeUnique<libais::Ais6_1_25>(body.c_str(), fill_bits);
        // TODO(schwehr): 28.
        // TODO(schwehr): 30.
        case 32:
          return MakeUnique<libais::Ais6_1_32>(body.c_str(), fill_bits);
        case 40:
          return MakeUnique<libais::Ais6_1_40>(body.c_str(), fill_bits);
      }
      // FI not handled.
      break;
  }
  return nullptr;
}

unique_ptr<AisMsg> CreateAisMsg8(const string &body, const int fill_bits) {
  libais::Ais8 msg(body.c_str(), fill_bits);
  switch (msg.dac) {
    // International Maritime Organization (IMO).
    case libais::AIS_DAC_1_INTERNATIONAL:
      switch (msg.fi) {
        case 0:
          return MakeUnique<libais::Ais8_1_0>(body.c_str(), fill_bits);
        case 11:
          return MakeUnique<libais::Ais8_1_11>(body.c_str(), fill_bits);
        case 13:
          return MakeUnique<libais::Ais8_1_13>(body.c_str(), fill_bits);
        case 15:
          return MakeUnique<libais::Ais8_1_15>(body.c_str(), fill_bits);
        case 16:
          return MakeUnique<libais::Ais8_1_16>(body.c_str(), fill_bits);
        case 17:
          return MakeUnique<libais::Ais8_1_17>(body.c_str(), fill_bits);
        case 19:
          return MakeUnique<libais::Ais8_1_19>(body.c_str(), fill_bits);
        case 21:
          return MakeUnique<libais::Ais8_1_21>(body.c_str(), fill_bits);
        case 22:
          return MakeUnique<libais::Ais8_1_22>(body.c_str(), fill_bits);
        case 24:
          return MakeUnique<libais::Ais8_1_24>(body.c_str(), fill_bits);
        case 26:
          return MakeUnique<libais::Ais8_1_26>(body.c_str(), fill_bits);
        case 27:
          return MakeUnique<libais::Ais8_1_27>(body.c_str(), fill_bits);
        case 29:
          return MakeUnique<libais::Ais8_1_29>(body.c_str(), fill_bits);
        case 31:
          return MakeUnique<libais::Ais8_1_31>(body.c_str(), fill_bits);
      }
      // FI not handled.
      break;
    // European River Information System (RIS).
    case libais::AIS_DAC_200_RIS:
      switch (msg.fi) {
        case 10:
          // Inland ship static and voyage related data
          return MakeUnique<libais::Ais8_200_10>(body.c_str(), fill_bits);
        case 21:
           // ETA at lock/bridge/terminal
          return MakeUnique<libais::Ais8_200_21>(body.c_str(), fill_bits);
        case 22:
          // RTA at lock/bridge/terminal
          return MakeUnique<libais::Ais8_200_22>(body.c_str(), fill_bits);
        case 23:
          // EMMA warning
          return MakeUnique<libais::Ais8_200_23>(body.c_str(), fill_bits);
        case 24:
           // Water levels
          return MakeUnique<libais::Ais8_200_24>(body.c_str(), fill_bits);
        case 40:
           // Signal status
          return MakeUnique<libais::Ais8_200_40>(body.c_str(), fill_bits);
        case 55:
           // Number of persons on board
          return MakeUnique<libais::Ais8_200_55>(body.c_str(), fill_bits);
      }
      // FI not handled.
      break;
    // TODO(schwehr): 366 US Coast Guard.
    case 367:  // US Coast Guard.
      switch (msg.fi) {
        case 22:
          return MakeUnique<libais::Ais8_367_22>(body.c_str(), fill_bits);
      }
      // FI not handled.
      break;
  }
  return nullptr;
}

unique_ptr<AisMsg> CreateAisMsg(const string &body, const int fill_bits) {
  if (body.empty()) {
    return nullptr;
  }
  if (fill_bits < 0 || fill_bits > 5) {
    return nullptr;
  }

  switch (body[0]) {
    case '1':  // FALLTHROUGH
    case '2':  // FALLTHROUGH
    case '3':  // 1-3: Class A position report.
      return MakeUnique<libais::Ais1_2_3>(body.c_str(), fill_bits);

    case '4':  // FALLTHROUGH - 4 - Basestation report
    case ';':  // 11 - UTC date response
      return MakeUnique<libais::Ais4_11>(body.c_str(), fill_bits);

    case '5':  // 5 - Ship and Cargo
      return MakeUnique<libais::Ais5>(body.c_str(), fill_bits);

    case '6':  // 6 - Addressed binary message
      return CreateAisMsg6(body, fill_bits);

    case '7':  // FALLTHROUGH - 7 - ACK for addressed binary message
    case '=':  // 13 - ASRM Ack  (safety message)
      return MakeUnique<libais::Ais7_13>(body.c_str(), fill_bits);

    case '8':  // 8 - Binary broadcast message (BBM)
      return CreateAisMsg8(body, fill_bits);

    case '9':  // 9 - SAR Position
      return MakeUnique<libais::Ais9>(body.c_str(), fill_bits);

    case ':':  //  10 - UTC Query
      return MakeUnique<libais::Ais10>(body.c_str(), fill_bits);

    // ';' 11 - See 4

    case '<':  // 12 - Addressed Safety Related Messages (ASRM)
      return MakeUnique<libais::Ais12>(body.c_str(), fill_bits);

    // '=' 13 - See 7

    case '>':  // 14 - Safety Related Broadcast Message (SRBM)
      return MakeUnique<libais::Ais14>(body.c_str(), fill_bits);

    case '?':  // 15 - Interrogation
      return MakeUnique<libais::Ais15>(body.c_str(), fill_bits);

    case '@':  // 16 - Assigned mode command
      return MakeUnique<libais::Ais16>(body.c_str(), fill_bits);

    case 'A':  // 17 - GNSS broadcast
      return MakeUnique<libais::Ais17>(body.c_str(), fill_bits);

    case 'B':  // 18 - Position, Class B
      return MakeUnique<libais::Ais18>(body.c_str(), fill_bits);

    case 'C':  // 19 - Position and ship, Class B
      return MakeUnique<libais::Ais19>(body.c_str(), fill_bits);

    case 'D':  // 20 - Data link management
      return MakeUnique<libais::Ais20>(body.c_str(), fill_bits);

    case 'E':  // 21 - Aids to navigation report
      return MakeUnique<libais::Ais21>(body.c_str(), fill_bits);

    case 'F':  // 22 - Channel Management
      return MakeUnique<libais::Ais22>(body.c_str(), fill_bits);

    case 'G':  // 23 - Group Assignment Command
      return MakeUnique<libais::Ais23>(body.c_str(), fill_bits);

    case 'H':  // 24 - Static data report
      return MakeUnique<libais::Ais24>(body.c_str(), fill_bits);

    case 'I':  // 25 - Single slot binary message
      return MakeUnique<libais::Ais25>(body.c_str(), fill_bits);

    case 'J':  // 26 - Multi slot binary message with comm state
      return MakeUnique<libais::Ais26>(body.c_str(), fill_bits);

    case 'K':  // 27 - Long-range AIS broadcast message
      return MakeUnique<libais::Ais27>(body.c_str(), fill_bits);

    default:
      return nullptr;
  }

  return nullptr;
}

}  // namespace libais
