/*---------------------------------------------------------------------------*/
/* media_tones.cpp                                                           */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"

#include "common/interface/socket.h"
#include "common/interface/media.h"
#include "common/interface/media_tones.h"
#include "physical.h"
#include "tones.h"

/*---------------------------------------------------------------------------*/

static struct {
    int id;
    const char *name;
} IdNameToneType[] =
{
    { (int)(&(((struct tones *)0)->dialing) - ((class ph_event_tone_on **)0)),         "dialing" },
    { (int)(&(((struct tones *)0)->dialing_special) - ((class ph_event_tone_on **)0)), "dialing_special" },
    { (int)(&(((struct tones *)0)->calling) - ((class ph_event_tone_on **)0)),         "calling" },
    { (int)(&(((struct tones *)0)->ringback) - ((class ph_event_tone_on **)0)),        "ringback" },
    { (int)(&(((struct tones *)0)->busy) - ((class ph_event_tone_on **)0)),            "busy" },
    { (int)(&(((struct tones *)0)->holding) - ((class ph_event_tone_on **)0)),         "holding" },
    { (int)(&(((struct tones *)0)->fast_busy) - ((class ph_event_tone_on **)0)),       "fast_busy" },
    { (int)(&(((struct tones *)0)->callwaiting) - ((class ph_event_tone_on **)0)),     "callwaiting" },
    { (int)(&(((struct tones *)0)->holding_cisco) - ((class ph_event_tone_on **)0)),   "holding_cisco" },
    { (int)(&(((struct tones *)0)->zipzip) - ((class ph_event_tone_on **)0)),          "zipzip" },
    { (int)(&(((struct tones *)0)->intrusion) - ((class ph_event_tone_on **)0)),       "intrusion" },
    { (int)(&(((struct tones *)0)->conference) - ((class ph_event_tone_on **)0)),      "conference" },
    { (int)(&(((struct tones *)0)->confirm) - ((class ph_event_tone_on **)0)),         "confirm" },
    { 0,                                                                               NULL },
};

static struct {
    char digit;
    AudioIoDualTone tone;
} DtmfToneTable[] =
{
    { '0', { 150, 150, 941, -10, 1336, -8 }},
    { '1', { 150, 150, 697, -10, 1209, -8 }},
    { '2', { 150, 150, 697, -10, 1336, -8 }},
    { '3', { 150, 150, 697, -10, 1477, -8 }},
    { '4', { 150, 150, 770, -10, 1209, -8 }},
    { '5', { 150, 150, 770, -10, 1336, -8 }},
    { '6', { 150, 150, 770, -10, 1477, -8 }},
    { '7', { 150, 150, 852, -10, 1209, -8 }},
    { '8', { 150, 150, 852, -10, 1336, -8 }},
    { '9', { 150, 150, 852, -10, 1477, -8 }},
    { '*', { 150, 150, 941, -10, 1209, -8 }},
    { '#', { 150, 150, 941, -10, 1477, -8 }},
    { 'A', { 150, 150, 697, -10, 1633, -8 }},
    { 'B', { 150, 150, 770, -10, 1633, -8 }},
    { 'C', { 150, 150, 852, -10, 1633, -8 }},
    { 'D', { 150, 150, 941, -10, 1633, -8 }},
};

static unsigned PrepareToneIndex(int toneIndex, int countryIndex, dword *flags, struct AudioIoDualTone *tones)
{
    unsigned count, pair, i;

    *flags = 0;
    class ph_event_tone_on * tone = ((class ph_event_tone_on **)(&tone_table[countryIndex]))[toneIndex];
    if (tone->tone_type & PH_TONE_TYPE_IN) {
        *flags |= AUDIO_IO_DUAL_TONE_BOTH;
    }
    if (tone->tone_type & PH_TONE_TYPE_OFF_TRANSPARENT) {
        *flags |= AUDIO_IO_DUAL_TONE_PASSTHROUGH;
    }
    if (tone->off_if_data) {
        *flags |= AUDIO_IO_DUAL_TONE_AUTOOFF;
    }
    pair = tone->tone_type & PH_TONE_TYPE_MASK;
    count = 0;
    if (tone->delay != 0) {
        tones[count].onTimeMs = 0;
        tones[count].offTimeMs = tone->delay;
        tones[count].frequency0 = 0;
        tones[count].level0 = -32768;
        tones[count].frequency1 = 0;
        tones[count].level1 = -32768;
        count++;
    }
    i = 0;
    while ((i < sizeof(tone->pattern) / sizeof(tone->pattern[0])) && (tone->pattern[i].on != 0)) {
        tones[count].onTimeMs = tone->pattern[i].on;
        tones[count].offTimeMs = (tone->pattern[i].off == 0xffff) ? 0 : tone->pattern[i].off;
        tones[count].frequency0 = tone_freq[tone_pair[pair].index_low];
        tones[count].level0 = (word)((tone_pair[pair].level_low >= 128) ? -32768 : -tone_pair[pair].level_low << 8);
        tones[count].frequency1 = tone_freq[tone_pair[pair].index_high];
        tones[count].level1 = (word)((tone_pair[pair].level_high >= 128) ? -32768 : -tone_pair[pair].level_high << 8);
        count++;
        i++;
    }
    if ((i == sizeof(tone->pattern) / sizeof(tone->pattern[0])) || (tone->pattern[i].off != 0xffff)) {
        *flags |= AUDIO_IO_DUAL_TONE_LOOP;
    }
    return count;
}

void MediaTones::StopTone(class IAudioIo *audioIo)
{
    audioIo->StopDualTones();
}

unsigned MediaTones::PrepareTone(int toneId, int countryId, dword *flags, struct AudioIoDualTone *tones)
{
    const char *countryName;
    int toneIndex, countryIndex;

    switch (toneId) {
    case MEDIA_TONES_DIALING:         toneIndex = (int)(&(((struct tones *)0)->dialing) - ((class ph_event_tone_on **)0));         break;
    case MEDIA_TONES_DIALING_SPECIAL: toneIndex = (int)(&(((struct tones *)0)->dialing_special) - ((class ph_event_tone_on **)0)); break;
    case MEDIA_TONES_CALLING:         toneIndex = (int)(&(((struct tones *)0)->calling) - ((class ph_event_tone_on **)0));         break;
    case MEDIA_TONES_RINGBACK:        toneIndex = (int)(&(((struct tones *)0)->ringback) - ((class ph_event_tone_on **)0));        break;
    case MEDIA_TONES_BUSY:            toneIndex = (int)(&(((struct tones *)0)->busy) - ((class ph_event_tone_on **)0));            break;
    case MEDIA_TONES_HOLDING:         toneIndex = (int)(&(((struct tones *)0)->holding) - ((class ph_event_tone_on **)0));         break;
    case MEDIA_TONES_FAST_BUSY:       toneIndex = (int)(&(((struct tones *)0)->fast_busy) - ((class ph_event_tone_on **)0));       break;
    case MEDIA_TONES_CALLWAITING:     toneIndex = (int)(&(((struct tones *)0)->callwaiting) - ((class ph_event_tone_on **)0));     break;
    case MEDIA_TONES_HOLDING_CISCO:   toneIndex = (int)(&(((struct tones *)0)->holding_cisco) - ((class ph_event_tone_on **)0));   break;
    case MEDIA_TONES_ZIPZIP:          toneIndex = (int)(&(((struct tones *)0)->zipzip) - ((class ph_event_tone_on **)0));          break;
    case MEDIA_TONES_INTRUSION:       toneIndex = (int)(&(((struct tones *)0)->intrusion) - ((class ph_event_tone_on **)0));       break;
    case MEDIA_TONES_CONFERENCE:      toneIndex = (int)(&(((struct tones *)0)->conference) - ((class ph_event_tone_on **)0));      break;
    case MEDIA_TONES_CONFIRM:         toneIndex = (int)(&(((struct tones *)0)->confirm) - ((class ph_event_tone_on **)0));         break;
    default:                          toneIndex = 0;                                                                               break;
    }
    switch (countryId) {
    case MEDIA_TONES_EUROPE_PBX:      countryName = "EUROPE-PBX";    break;
    case MEDIA_TONES_EUROPE_PUBLIC:   countryName = "EUROPE-PUBLIC"; break;
    case MEDIA_TONES_US:              countryName = "US";            break;
    case MEDIA_TONES_UK:              countryName = "UK";            break;
    case MEDIA_TONES_ITALY_PBX:       countryName = "ITALY-PBX";     break;
    case MEDIA_TONES_ITALY_PUBLIC:    countryName = "ITALY-PUBLIC";  break;
    case MEDIA_TONES_CZECH_PBX:       countryName = "CZECH-PBX";     break;
    case MEDIA_TONES_CZECH_PUBLIC:    countryName = "CZECH-PUBLIC";  break;
    case MEDIA_TONES_SWEDEN:          countryName = "SWEDEN";        break;
    case MEDIA_TONES_FRANCE:          countryName = "FRANCE";        break;
    case MEDIA_TONES_SWISS:           countryName = "SWISS";         break;
    case MEDIA_TONES_BELGIUM:         countryName = "BELGIUM";       break;
    case MEDIA_TONES_NETHERLANDS:     countryName = "NETHERLANDS";   break;
    case MEDIA_TONES_NORWAY:          countryName = "NORWAY";        break;
    case MEDIA_TONES_DENMARK:         countryName = "DENMARK";       break;
    case MEDIA_TONES_GERMANY:         countryName = "GERMANY";       break;
    case MEDIA_TONES_SPAIN:           countryName = "SPAIN";         break;
    case MEDIA_TONES_FINLAND:         countryName = "FINLAND";       break;
    case MEDIA_TONES_AUSTRIA:         countryName = "AUSTRIA";       break;
    case MEDIA_TONES_IRELAND:         countryName = "IRELAND";       break;
    case MEDIA_TONES_AUSTRALIA:       countryName = "AUSTRALIA";     break;
    case MEDIA_TONES_NEWZEALAND:      countryName = "NEWZEALAND";    break;
    case MEDIA_TONES_MALAYSIA:        countryName = "MALAYSIA";      break;
    case MEDIA_TONES_TURKEY:          countryName = "TURKEY";        break;
    case MEDIA_TONES_RUSSIA:          countryName = "RUSSIA";        break;
    case MEDIA_TONES_SOUTH_AFRICA:    countryName = "SOUTH AFRICA";  break;
    case MEDIA_TONES_BRAZIL:          countryName = "BRAZIL";        break;
    default:                          countryName = NULL;            break;
    }
    countryIndex = 0;
    if (countryName) {
        while ((countryIndex < PHONE_MAX_TONES) && (strcmp(countryName, tone_title[countryIndex]) != 0)) {
            countryIndex++;
        }
        if (countryIndex == PHONE_MAX_TONES) {
            countryIndex = 0;
        }
    }
    return PrepareToneIndex(toneIndex, countryIndex, flags, tones);
}

void MediaTones::StartTone(int toneId, int countryId, class IAudioIo *audioIo)
{
    struct AudioIoDualTone tones[16];
    dword flags;
    unsigned count = PrepareTone(toneId, countryId, &flags, tones);
    audioIo->StartDualTones(flags, count, tones);
}

unsigned MediaTones::PrepareToneMsg(const char *toneName, const char *countryName, dword *flags, struct AudioIoDualTone *tones)
{
    int toneIndex, countryIndex;
    unsigned i;
    
    i = 0;
    if (!toneName) {
        while (IdNameToneType[i].name) {
            i++;
        }
    }
    else {
        while (IdNameToneType[i].name && (strcmp(IdNameToneType[i].name, toneName) != 0)) {
            i++;
        }
    }
    toneIndex = IdNameToneType[i].id;
    countryIndex = 0;
    if (countryName) {
        while ((countryIndex < PHONE_MAX_TONES) && (strcmp(countryName, tone_title[countryIndex]) != 0)) {
            countryIndex++;
        }
        if (countryIndex == PHONE_MAX_TONES) {
            countryIndex = 0;
        }
    }
    return PrepareToneIndex(toneIndex, countryIndex, flags, tones);
}

void MediaTones::StartToneMsg(const char *toneName, const char *countryName, class IAudioIo *audioIo)
{
    struct AudioIoDualTone tones[16];
    dword flags;
    unsigned count = PrepareToneMsg(toneName, countryName, &flags, tones);
    audioIo->StartDualTones(flags, count, tones);
}

unsigned MediaTones::PrepareDtmf(char digit, struct AudioIoDualTone *tones)
{
    unsigned count, i;

    count = 0;
    i = 0;
    while ((i < sizeof(DtmfToneTable) / sizeof(DtmfToneTable[0])) && (DtmfToneTable[i].digit != digit)) {
        i++;
    }
    if (i < sizeof(DtmfToneTable) / sizeof(DtmfToneTable[0])) {
        memcpy(tones, &DtmfToneTable[i].tone, sizeof(struct AudioIoDualTone));
        count++;
    }
    return count;
}

void MediaTones::StartDtmf(char digit, bool feedback, class IAudioIo *audioIo)
{
    struct AudioIoDualTone tones[1];
    unsigned count = PrepareDtmf(digit, tones);
    if (count != 0) {
        audioIo->StartDualTones(feedback ? AUDIO_IO_DUAL_TONE_BOTH | AUDIO_IO_DUAL_TONE_PEER : AUDIO_IO_DUAL_TONE_PEER, count, tones);
    }
}

/*---------------------------------------------------------------------------*/
