/*****************************************************************************

    mhimdev.library - MHI driver for mpeg.device
    Copyright (C) 2001  Michael Henke

    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*****************************************************************************/


#include <proto/exec.h>
#include <proto/dos.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <utility/tagitem.h>
#include <libraries/mhi.h>
#include <devices/melodympeg.h>
#include "mhimdev.h"

static void myDoIO(struct IOMPEGReq *mdevreq, UWORD command);
static void __asm softint_server(register __a1 struct mhimdevHandle *h);




struct mhimdevHandle
{
    UBYTE               mdevunit, mdevname[256], mhistatus;
    struct MsgPort      *mdevport;
    struct IOMPEGReq    *mdevreq;
    struct List         list_unused, list_getempty;
    struct Task         *mhitask;
    ULONG               mhisignal;
    struct Interrupt    mdevsoftint;
    LONG                requests_pending;
    ULONG               mhivolume, mhipanning;
};





APTR __asm
i_MHIAllocDecoder(register __a0 struct Task *task, register __d0 ULONG mhisignal)
{
    struct mhimdevHandle *h;
#ifdef DEBUG
KPutStr("MHIAllocDecoder()\n");
#endif
    if(task==NULL) return(NULL);
    h=(struct mhimdevHandle*)AllocVec(sizeof(struct mhimdevHandle),MEMF_PUBLIC|MEMF_CLEAR);
    if(h==NULL) return(NULL);
    h->mhitask=task;
    h->mhisignal=mhisignal;
    h->mhistatus=MHIF_STOPPED;
    h->mhivolume=100;
    h->mhipanning=50;
    NewList(&h->list_unused);
    NewList(&h->list_getempty);
    {
        struct DosLibrary *DOSBase;
        UBYTE *varbuf;
        LONG  varlen, args[2]={0,0};
        struct RDArgs *rdargs, *rdargs2;
        struct TagItem tagdone={TAG_DONE,0};
        if((DOSBase=(struct DosLibrary*)OpenLibrary("dos.library",37)))
        {
            if((varbuf=AllocVec(1024,MEMF_PUBLIC|MEMF_CLEAR)))
            {
                if((varlen=GetVar("MHIMDEV",varbuf,1024,LV_VAR))>0)
                {
#ifdef DEBUG
KPutStr("MHIMDEV varbuf=\"");
KPutStr(varbuf);
KPutStr("\"\n");
#endif
                    if((rdargs=AllocDosObject(DOS_RDARGS,&tagdone)))
                    {
                        rdargs->RDA_Source.CS_Buffer=varbuf;
                        rdargs->RDA_Source.CS_Length=varlen;
                        rdargs->RDA_Source.CS_CurChr=0;
                        rdargs->RDA_Flags=RDAF_NOPROMPT;
                        rdargs->RDA_DAList=0;
                        rdargs2=ReadArgs("DEVICE,UNIT/N",args,rdargs);
                        if(args[0]) CopyMem((APTR)args[0],h->mdevname,sizeof(h->mdevname)-1);
                        if(args[1]) h->mdevunit=(*(LONG*)args[1]);
                        FreeArgs(rdargs2);
                        FreeDosObject(DOS_RDARGS,rdargs);
                    }
                }
                FreeVec(varbuf);
            }
            CloseLibrary((struct Library*)DOSBase);
        }
    }
#ifdef DEBUG
KPutStr("MHIMDEV var:  DEVICE=");
KPutStr(h->mdevname);
KPutStr("  UNIT=");
KPutChar((LONG)('0'+h->mdevunit));
#endif
    if((h->mdevport=CreateMsgPort()))
    {
        h->mdevsoftint.is_Data=(APTR)h;
        h->mdevsoftint.is_Code=(void(*)(void))softint_server;
        h->mdevport->mp_SoftInt= &h->mdevsoftint;
        h->mdevport->mp_Flags=PA_SOFTINT;
        if((h->mdevreq=(struct IOMPEGReq*)CreateIORequest(h->mdevport,sizeof(struct IOMPEGReq))))
        {
            if(OpenDevice(h->mdevname,(ULONG)h->mdevunit,(struct IORequest*)h->mdevreq,0)==0)
            {
                myDoIO(h->mdevreq,CMD_RESET);
                myDoIO(h->mdevreq,MPEGCMD_PLAY);
                h->mdevreq->iomr_Arg1=1;
                myDoIO(h->mdevreq,MPEGCMD_PAUSE);
#ifdef DEBUG
KPutStr("  __open ok__\n");
#endif
                return(h);
            }
            DeleteIORequest(h->mdevreq);
        }
        DeleteMsgPort(h->mdevport);
    }
    FreeVec(h);
#ifdef DEBUG
KPutStr("  **open failed**\n");
#endif
    return(NULL);
}





void __asm
i_MHIFreeDecoder(register __a3 APTR handle)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
    struct IOMPEGReq *req;
#ifdef DEBUG
KPutStr("MHIFreeDecoder()\n");
#endif
    if(handle==NULL) return;
    i_MHIStop(handle);
    while((req=(struct IOMPEGReq*)RemHead(&h->list_unused)))
        FreeMem(req,sizeof(struct IOMPEGReq));
    CloseDevice((struct IORequest*)h->mdevreq);
    DeleteIORequest(h->mdevreq);
    DeleteMsgPort(h->mdevport);
    FreeVec(h);
}





BOOL __asm
i_MHIQueueBuffer(register __a3 APTR handle, register __a0 APTR buffer, register __d0 ULONG size)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
    struct IOMPEGReq *req;
#ifdef DEBUG
KPutStr("MHIQueueBuffer()\n");
#endif
    if((handle==NULL) || (buffer==NULL) || (size==0)) return(FALSE);
    if(!(req=(struct IOMPEGReq*)RemHead(&h->list_unused)))
    {
        UWORD i;
        for(i=0;i<32;i++)
        {
            req=(struct IOMPEGReq*)AllocMem(sizeof(struct IOMPEGReq),MEMF_PUBLIC);
            if(req) AddTail(&h->list_unused,(struct Node*)req);
        }
        if(!(req=(struct IOMPEGReq*)RemHead(&h->list_unused)))
            return(FALSE);
    }
    CopyMem(h->mdevreq,req,sizeof(struct IOMPEGReq));
    req->iomr_Req.io_Command=CMD_WRITE;
    req->iomr_Req.io_Data=buffer;
    req->iomr_Req.io_Length=size;
    req->iomr_Req.io_Flags=0;
    req->iomr_StreamType=MPEGSTREAM_AUDIO;
    req->iomr_MPEGFlags=0;
    h->requests_pending++;
    SendIO((struct IORequest*)req);
    return(TRUE);
}





APTR __asm
i_MHIGetEmpty(register __a3 APTR handle)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
    struct IOMPEGReq *req;
#ifdef DEBUG
KPutStr("MHIGetEmpty()\n");
#endif
    if(handle==NULL) return(NULL);
    Disable();
    req=(struct IOMPEGReq*)RemHead(&h->list_getempty);
    Enable();
    if(req==NULL) return(NULL);
    AddTail(&h->list_unused,(struct Node*)req);
    return(req->iomr_Req.io_Data);
}





UBYTE __asm
i_MHIGetStatus(register __a3 APTR handle)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
#ifdef DEBUG
KPutStr("MHIGetStatus()\n");
#endif
    if(handle==NULL) return(MHIF_STOPPED);
    if((h->requests_pending<=0) && (h->mhistatus==MHIF_PLAYING))
        return(MHIF_OUT_OF_DATA);
    else
        return(h->mhistatus);
}





void __asm
i_MHIPlay(register __a3 APTR handle)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
#ifdef DEBUG
KPutStr("MHIPlay()\n");
#endif
    if(handle==NULL) return;
    myDoIO(h->mdevreq,MPEGCMD_PLAY);
    h->mdevreq->iomr_Arg1=0;
    myDoIO(h->mdevreq,MPEGCMD_PAUSE);
    h->mhistatus=MHIF_PLAYING;
}





void __asm
i_MHIStop(register __a3 APTR handle)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
    struct IOMPEGReq *req;
#ifdef DEBUG
KPutStr("MHIStop()\n");
#endif
    if(handle==NULL) return;
    Disable();
    h->mdevport->mp_Flags=PA_IGNORE;
    Enable();
    while((req=(struct IOMPEGReq*)RemHead(&h->list_getempty)))
        AddTail(&h->list_unused,(struct Node*)req);
    myDoIO(h->mdevreq,CMD_FLUSH);
    while((req=(struct IOMPEGReq*)GetMsg(h->mdevport)))
        if(req!=h->mdevreq) AddTail(&h->list_unused,(struct Node*)req);
    h->requests_pending=0;
    h->mdevreq->iomr_Arg1=1;
    myDoIO(h->mdevreq,MPEGCMD_PAUSE);
    h->mdevport->mp_Flags=PA_SOFTINT;
    h->mhistatus=MHIF_STOPPED;
}





void __asm
i_MHIPause(register __a3 APTR handle)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
#ifdef DEBUG
KPutStr("MHIPause()\n");
#endif
    if(handle==NULL) return;
    if(h->mhistatus==MHIF_PLAYING)
    {
        h->mdevreq->iomr_Arg1=1;
        h->mhistatus=MHIF_PAUSED;
    }
    else if(h->mhistatus==MHIF_PAUSED)
    {
        h->mdevreq->iomr_Arg1=0;
        h->mhistatus=MHIF_PLAYING;
    }
    else return;
    myDoIO(h->mdevreq,MPEGCMD_PAUSE);
}





ULONG __asm
i_MHIQuery(register __d1 ULONG query)
{
#ifdef DEBUG
KPutStr("MHIQuery()\n");
#endif
    switch(query)
    {
    case MHIQ_DECODER_NAME:
        return((ULONG)"MHI driver for mpeg.device");
    case MHIQ_DECODER_VERSION:
        return((ULONG)IDSTRING);
    case MHIQ_AUTHOR:
        return((ULONG)"Michael Henke");
    case MHIQ_IS_HARDWARE:
        return(MHIF_TRUE);
    case MHIQ_LAYER1:
    case MHIQ_LAYER2:
    case MHIQ_LAYER3:
        /* mpeg.device is able to tell us but we can't access it from here */
        /* we would need an initialised mhimdevHandle for this... */
        return(MHIF_SUPPORTED);
    case MHIQ_MPEG1:
    case MHIQ_MPEG2:
    case MHIQ_MPEG25:
    case MHIQ_VARIABLE_BITRATE:
    case MHIQ_JOINT_STERIO:
        /* mpeg.device can't tell us these things but we are optimistic */
        return(MHIF_SUPPORTED);
    case MHIQ_VOLUME_CONTROL:
    case MHIQ_PANNING_CONTROL:
        /* these are standard features of mpeg.device */
        return(MHIF_SUPPORTED);
    default:
        /* everything else is either MHIF_FALSE or MHIF_UNSUPPORTED ;-) */
        return(MHIF_UNSUPPORTED);
    }
}





void __asm
i_MHISetParam(register __a3 APTR handle, register __d0 UWORD param, register __d1 ULONG value)
{
    struct mhimdevHandle *h = (struct mhimdevHandle*) handle;
    struct MPEGAudioParams map;
    ULONG vol;
#ifdef DEBUG
KPutStr("MHISetParam()\n");
#endif
    if(handle==NULL) return;
    if(param==MHIP_VOLUME) h->mhivolume=(value>100 ? 100 : value);
    else if(param==MHIP_PANNING) h->mhipanning=(value>100 ? 100 : value);
    else return;
    vol=0xffff*h->mhivolume;
    map.map_VolumeLeft =(vol*(ULONG)(h->mhipanning>50 ? 100-h->mhipanning : 50))/5000;
    map.map_VolumeRight=(vol*(ULONG)(h->mhipanning<50 ? h->mhipanning     : 50))/5000;
    map.map_StreamID=0xffff;
    h->mdevreq->iomr_Req.io_Data= &map;
    h->mdevreq->iomr_Req.io_Length=sizeof(struct MPEGAudioParams);
    myDoIO(h->mdevreq,MPEGCMD_SETAUDIOPARAMS);
}





/*** private routines ***/

static void
myDoIO(struct IOMPEGReq *mdevreq, UWORD command)
{
    mdevreq->iomr_Req.io_Command=command;
    mdevreq->iomr_Req.io_Flags=0;
    mdevreq->iomr_StreamType=MPEGSTREAM_AUDIO;
    DoIO((struct IORequest*)mdevreq);
}


static void __asm
softint_server(register __a1 struct mhimdevHandle *h)
{
    struct IOMPEGReq *req;
#ifdef DEBUG
KPutStr("--softint_server\n");
#endif
    while((req=(struct IOMPEGReq*)GetMsg(h->mdevport)))
    {
        if(req!=h->mdevreq)
        {
            AddTail(&h->list_getempty,(struct Node*)req);
            h->requests_pending--;
        }
    }
    Signal(h->mhitask,h->mhisignal);
}

