//
//  cftdidevicedescr.cpp
//  ambed
//
//  Created by Jean-Luc Deltombe (LX3JL) on 02/06/2017.
//  Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
//
// ----------------------------------------------------------------------------
//    This file is part of ambed.
//
//    xlxd is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    xlxd is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------

#include "main.h"
#include <string.h>
#include "cusb3000interface.h"
#include "cusb3003interface.h"
#include "cusb3003hrinterface.h"
#include "cusb3003df2etinterface.h"
#include "cftdidevicedescr.h"


////////////////////////////////////////////////////////////////////////////////////////
// constructor

CFtdiDeviceDescr::CFtdiDeviceDescr(void)
{
    m_bUsed = false;
    m_uiVid = 0;
    m_uiPid = 0;
    ::memset(m_szDescription, 0, sizeof(m_szDescription));
    ::memset(m_szSerial, 0, sizeof(m_szSerial));
}

CFtdiDeviceDescr::CFtdiDeviceDescr(uint32 uiVid, uint32 uiPid, const char *szDescription, const char *szSerial)
{
    m_bUsed = false;
    m_uiVid = uiVid;
    m_uiPid = uiPid;
    ::strcpy(m_szDescription, szDescription);
    ::strcpy(m_szSerial, szSerial);
}

CFtdiDeviceDescr::CFtdiDeviceDescr(const CFtdiDeviceDescr &descr)
{
    m_bUsed = descr.m_bUsed;
    m_uiVid = descr.m_uiVid;
    m_uiPid = descr.m_uiPid;
    ::memcpy(m_szDescription, descr.m_szDescription, sizeof(m_szDescription));
    ::memcpy(m_szSerial, descr.m_szSerial, sizeof(m_szSerial));
}

////////////////////////////////////////////////////////////////////////////////////////
// interface factory

int CFtdiDeviceDescr::CreateInterface(CFtdiDeviceDescr *descr, std::vector<CVocodecChannel *>*channels)
{
    int iNbChs = 0;
    
    // single channel devices cannot be created alone
    // three channels devices
    if ( (::strcmp(descr->GetDescription(), "USB-3003")    == 0) ||     // DVSI's  USB-3003
         (::strcmp(descr->GetDescription(), "DF2ET-3003")  == 0) ||     // DF2ET's USB-3003 opensource device
         (::strcmp(descr->GetDescription(), "DVstick-33")  == 0) ||     // DVMEGA USB-3003 device  
         (::strcmp(descr->GetDescription(), "ThumbDV-3")   == 0) )      // ThumbDV-3
    {
        iNbChs = CreateUsb3003(descr, channels);
    }
    // six channels devices
    else if ( (::strcmp(descr->GetDescription(), "USB-3006 A") == 0) )  // LX3JL's USB-3006 opensource device
    {
        iNbChs = CreateUsb3006(descr, channels);
    }
    // twelves channels devices
    else if ( (::strcmp(descr->GetDescription(), "USB-3012 A") == 0) )  // DVSI's USB-3012
    {
        iNbChs = CreateUsb3012(descr, channels);
    }
    // done
    return iNbChs;
}

int CFtdiDeviceDescr::CreateInterfacePair(CFtdiDeviceDescr *descr1, CFtdiDeviceDescr *descr2, std::vector<CVocodecChannel *>*channels)
{
    int iNbChs = 0;

    // create interface objects
    if ( (descr1->GetNbChannels() == 1) && (descr2->GetNbChannels() == 1) )
    {
        // create 3000-3000 pair
        CUsb3000Interface *Usb3000A = InstantiateUsb3000(descr1);
        CUsb3000Interface *Usb3000B = InstantiateUsb3000(descr2);
        iNbChs = CreatePair(Usb3000A, Usb3000B, channels);
    }
    else if ( (descr1->GetNbChannels() == 3) && (descr2->GetNbChannels() == 1) )
    {
        // create 3003-3000 pair
        CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr1);
        CUsb3000Interface *Usb3000 = InstantiateUsb3000(descr2);
        iNbChs = CreatePair(Usb3003, Usb3000, channels);
   }
    else if ( (descr1->GetNbChannels() == 1) && (descr2->GetNbChannels() == 3) )
    {
        // create 3000-3003 pair
        CUsb3000Interface *Usb3000 = InstantiateUsb3000(descr1);
        CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr2);
        iNbChs = CreatePair(Usb3003, Usb3000, channels);
    }
    else if ( (descr1->GetNbChannels() == 3) && (descr2->GetNbChannels() == 3) )
    {
        // create 3003-3003 pair
        CUsb3003Interface *Usb3003A = InstantiateUsb3003(descr1);
        CUsb3003Interface *Usb3003B = InstantiateUsb3003(descr2);
        iNbChs = CreatePair(Usb3003A, Usb3003B, channels);
    }
    
    // done
    return iNbChs;
}

////////////////////////////////////////////////////////////////////////////////////////
// get

const char * CFtdiDeviceDescr::GetChannelDescription(int ch) const
{
    static char descr[FTDI_MAX_STRINGLENGTH];
    char tag[3] = "_X";
    
    ::strcpy(descr, GetDescription());
    if ( ::strlen(descr) >= 2 )
    {
        descr[::strlen(descr)-2] = 0x00;
        tag[1] = (char)ch + 'A';
        ::strcat(descr, tag);
    }
    return descr;
}

const char * CFtdiDeviceDescr::GetChannelSerialNumber(int ch) const
{
    static char serial[FTDI_MAX_STRINGLENGTH];
    
    ::strcpy(serial, GetSerialNumber());
    if ( ::strlen(serial) >= 1 )
    {
        serial[::strlen(serial)-1] = (char)ch + 'A';
    }
    return serial;
}

int CFtdiDeviceDescr::GetNbChannels(void) const
{
    int iNbChs = 0;
    
    // single channel devices
    if ( (::strcmp(m_szDescription, "USB-3000")   == 0) ||           // DVSI's USB-3000
         (::strcmp(m_szDescription, "DVstick-30") == 0) ||           // DVMEGA AMBE3000 device
         (::strcmp(m_szDescription, "ThumbDV")    == 0) )            // ThumbDV
    {
        iNbChs = 1;
    }
    // three channels devices
    else if ( (::strcmp(m_szDescription, "USB-3003")    == 0) ||     // DVSI's  USB-3003
              (::strcmp(m_szDescription, "DF2ET-3003")  == 0) ||     // DF2ET's USB-3003 opensource device
              (::strcmp(m_szDescription, "DVstick-33")  == 0) ||     // DVMEGA AMBE 3003 device
              (::strcmp(m_szDescription, "ThumbDV-3")   == 0) )      // ThumbDV-3
    {
        iNbChs = 3;
    }
    // six channels devices
    else if ( (::strcmp(m_szDescription, "USB-3006 A") == 0) )       // LX3JL's USB-3006 opensource device
    {
        iNbChs = 6;
    }
    // twelves channels devices
    else if ( (::strcmp(m_szDescription, "USB-3012 A") == 0) )       // DVSI's USB-3012
    {
        iNbChs = 12;
    }
  
    // done
    return iNbChs;
}

////////////////////////////////////////////////////////////////////////////////////////
// DVSI's USB-3012 factory helper
//
//      This device uses 3 AMBE3003 connected on a single FTDI 4 channels
//      USB to serial interface. The reset mechanism is based
//      on DTR and SetBreak. Baudrate is 921600
//

int CFtdiDeviceDescr::CreateUsb3012(CFtdiDeviceDescr *descr, std::vector<CVocodecChannel *>*channels)
{
    int nStreams = 0;
    
    // create the interfaces for the four 3003 chips
    CUsb3003HRInterface *Usb3003A =
        new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(0), descr->GetChannelSerialNumber(0));
    CUsb3003HRInterface *Usb3003B =
        new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(1), descr->GetChannelSerialNumber(1));
    CUsb3003HRInterface *Usb3003C =
        new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(2), descr->GetChannelSerialNumber(2));
    CUsb3003HRInterface *Usb3003D =
        new CUsb3003HRInterface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(3), descr->GetChannelSerialNumber(3));
    
    // init the interfaces
    if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) &&
         Usb3003C->Init(CODEC_AMBEPLUS) && Usb3003D->Init(CODEC_AMBE2PLUS) )
    {
        CVocodecChannel *Channel;
        // create all channels
        {
            // ch1
            Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch2
            Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch3
            Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003B->AddChannel(Channel);
            // ch4
            Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003B->AddChannel(Channel);
            // ch5
            Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3003B->AddChannel(Channel);
            // ch6
            Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3003B->AddChannel(Channel);
            // ch7
            Channel = new CVocodecChannel(Usb3003C, 0, Usb3003C, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003C->AddChannel(Channel);
            // ch8
            Channel = new CVocodecChannel(Usb3003C, 1, Usb3003C, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003C->AddChannel(Channel);
            // ch9
            Channel = new CVocodecChannel(Usb3003D, 0, Usb3003D, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003D->AddChannel(Channel);
            // ch10
            Channel = new CVocodecChannel(Usb3003D, 1, Usb3003D, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003D->AddChannel(Channel);
            // ch11
            Channel = new CVocodecChannel(Usb3003C, 2, Usb3003D, 2, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003C->AddChannel(Channel);
            Usb3003D->AddChannel(Channel);
            // ch12
            Channel = new CVocodecChannel(Usb3003D, 2, Usb3003C, 2, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003C->AddChannel(Channel);
            Usb3003D->AddChannel(Channel);
            //done
            nStreams = 12;
        }
    }
    else
    {
        // cleanup
        delete Usb3003A;
        delete Usb3003B;
        delete Usb3003C;
        delete Usb3003D;
    }
    
    // done
    return nStreams;
}

////////////////////////////////////////////////////////////////////////////////////////
// LX3JL's USB-3006 factory helper
//
//      This device uses 2 AMBE3003 connected on a single FTDI 2 channels
//      USB to serial interface. The reset mechanism is based
//      on DTR and software reset. Baudrate is 921600
//
int CFtdiDeviceDescr::CreateUsb3006(CFtdiDeviceDescr *descr, std::vector<CVocodecChannel *>*channels)
{
    int  nStreams = 0;
    
    // create the interfaces for the two 3003 chips
    CUsb3003Interface *Usb3003A =
        new CUsb3003Interface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(0), descr->GetChannelSerialNumber(0));
    CUsb3003Interface *Usb3003B =
        new CUsb3003Interface(descr->GetVid(), descr->GetPid(), descr->GetChannelDescription(1), descr->GetChannelSerialNumber(1));
    
    // init the interfaces
    if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) )
    {
        CVocodecChannel *Channel;
        // create all channels
        {
            // ch1
            Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch2
            Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch3
            Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003B->AddChannel(Channel);
            // ch4
            Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003B->AddChannel(Channel);
            // ch5
            Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3003B->AddChannel(Channel);
            // ch6
            Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3003B->AddChannel(Channel);
            //done
            nStreams = 6;
        }
    }
    else
    {
        // cleanup
        delete Usb3003A;
        delete Usb3003B;
    }
    
    // done
    return nStreams;
}

////////////////////////////////////////////////////////////////////////////////////////
// USB-3003 factory helpers
//   DVSI
//   DF2ET
//   ThumbDV
//   DVMEGA
//
//      These devices uses a AMBE3003 connected on a single FTDI
//      USB to serial interface. The reset mechanism is based
//      on DTR and SetBreak, or software. Baudrate is 921600
//
int CFtdiDeviceDescr::CreateUsb3003(CFtdiDeviceDescr *descr, std::vector<CVocodecChannel *>*channels)
{
    int nStreams = 0;
    
    // create the interfaces for the 3003 chip
    CUsb3003Interface *Usb3003 = InstantiateUsb3003(descr);
    
    // init the interface
    if ( (Usb3003 != NULL) && Usb3003->Init(CODEC_NONE) )
    {
        CVocodecChannel *Channel;
        // create all channels
        {
            // ch1
            Channel = new CVocodecChannel(Usb3003, 0, Usb3003, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003->AddChannel(Channel);
            // ch2
            Channel = new CVocodecChannel(Usb3003, 1, Usb3003, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003->AddChannel(Channel);
            // done
            nStreams = 2;
        }
    }
    else
    {
        // cleanup
        delete Usb3003;
    }
    
    // done
    return nStreams;
}


////////////////////////////////////////////////////////////////////////////////////////
// 1 ch + 1 ch pair creation

int CFtdiDeviceDescr::CreatePair(CUsb3000Interface *Usb3000A, CUsb3000Interface *Usb3000B, std::vector<CVocodecChannel *>*channels)
{
    int nStreams = 0;
    
    // init the interfaces
    if ( Usb3000A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) )
    {
        CVocodecChannel *Channel;
        // create all channels
        {
            // ch1
            Channel = new CVocodecChannel(Usb3000A, 0, Usb3000B, 0, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3000A->AddChannel(Channel);
            Usb3000B->AddChannel(Channel);
            // ch2
            Channel = new CVocodecChannel(Usb3000B, 0, Usb3000A, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3000A->AddChannel(Channel);
            Usb3000B->AddChannel(Channel);
            // done
            nStreams = 2;
        }
    }
    else
    {
        // cleanup
        delete Usb3000A;
        delete Usb3000B;
    }
    
    // done
    return nStreams;
   
}

////////////////////////////////////////////////////////////////////////////////////////
// 3 ch + 3 ch pair creation

int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3003Interface *Usb3003B, std::vector<CVocodecChannel *>*channels)
{
    int nStreams = 0;
    
    // init the interfaces
    if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3003B->Init(CODEC_AMBE2PLUS) )
    {
        CVocodecChannel *Channel;
        // create all channels
        {
            // ch1
            Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch2
            Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch3
            Channel = new CVocodecChannel(Usb3003B, 0, Usb3003B, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003B->AddChannel(Channel);
            // ch4
            Channel = new CVocodecChannel(Usb3003B, 1, Usb3003B, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003B->AddChannel(Channel);
            // ch5
            Channel = new CVocodecChannel(Usb3003A, 2, Usb3003B, 2, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3003B->AddChannel(Channel);
            // ch6
            Channel = new CVocodecChannel(Usb3003B, 2, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3003B->AddChannel(Channel);
            // done
            nStreams = 6;
        }
    }
    else
    {
        // cleanup
        delete Usb3003A;
        delete Usb3003B;
    }
    
    // done
    return nStreams;
}

////////////////////////////////////////////////////////////////////////////////////////
// 3 ch + 1 ch pair creation

int CFtdiDeviceDescr::CreatePair(CUsb3003Interface *Usb3003A, CUsb3000Interface *Usb3000B, std::vector<CVocodecChannel *>*channels)
{
    int nStreams = 0;
    
    // init the interfaces
    if ( Usb3003A->Init(CODEC_AMBEPLUS) && Usb3000B->Init(CODEC_AMBE2PLUS) )
    {
        CVocodecChannel *Channel;
        // create all channels
        {
            // ch1
            Channel = new CVocodecChannel(Usb3003A, 0, Usb3003A, 1, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch2
            Channel = new CVocodecChannel(Usb3003A, 1, Usb3003A, 0, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            // ch3
            Channel = new CVocodecChannel(Usb3003A, 2, Usb3000B, 0, CODECGAIN_AMBEPLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3000B->AddChannel(Channel);
            // ch4
            Channel = new CVocodecChannel(Usb3000B, 0, Usb3003A, 2, CODECGAIN_AMBE2PLUS);
            channels->push_back(Channel);
            Usb3003A->AddChannel(Channel);
            Usb3000B->AddChannel(Channel);
            // done
            nStreams = 4;
        }
    }
    else
    {
        // cleanup
        delete Usb3003A;
        delete Usb3000B;
    }
    
    // done
    return nStreams;
}


////////////////////////////////////////////////////////////////////////////////////////
// interface instantiation helpers

CUsb3003Interface *CFtdiDeviceDescr::InstantiateUsb3003(CFtdiDeviceDescr *descr)
{
    CUsb3003Interface *Usb3003 = NULL;
    
    // intstantiate the proper version of USB-3003
    if ( (::strcmp(descr->GetDescription(), "USB-3003") == 0) )              // DVSI's  USB-3003
    {
        // hardware reset, 921600 bps
        Usb3003 = new CUsb3003HRInterface
            (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber());
    }
    else if ( (::strcmp(descr->GetDescription(), "DF2ET-3003") == 0) )       // DF2ET's USB-3003 opensource device
    {
        // specific hardware reset, 921600 bps
        Usb3003 = new CUsb3003DF2ETInterface
            (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber());
    }
    else if ( (::strcmp(descr->GetDescription(), "ThumbDV-3")   == 0) )      // ThumbDV-3
    {
        // software reset, 921600 bps
        Usb3003 = new CUsb3003Interface
            (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber());
    }
    else if ( (::strcmp(descr->GetDescription(), "DVstick-33")  == 0) )      // DVMEGA AMBE3003 device
    {
        // specific fardware reset, 921600 bps
        Usb3003 = new CUsb3003DF2ETInterface
            (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber());
    }

    // done
    return Usb3003;
}

CUsb3000Interface *CFtdiDeviceDescr::InstantiateUsb3000(CFtdiDeviceDescr *descr)
{
    CUsb3000Interface *Usb3000 = NULL;
    
    // intstantiate the proper version of USB-3000
    if ( (::strcmp(descr->GetDescription(), "USB-3000")  == 0) ||           // DVSI's USB-3000
         (::strcmp(descr->GetDescription(), "DVstick-30")== 0) ||           // DVMEGA AMBE3000 device
         (::strcmp(descr->GetDescription(), "ThumbDV")   == 0) )            // ThumbDV
   {
        Usb3000 = new CUsb3000Interface
            (descr->GetVid(), descr->GetPid(), descr->GetDescription(), descr->GetSerialNumber());
    }
    // done
    return Usb3000;
}
