using Contralto.IO;
using Contralto.Logging;
using System;

namespace Contralto.CPU
{
    public partial class AltoCPU
    {
        /// <summary>
        /// EthernetTask implements Ethernet-specific task function
        /// </summary>
        private sealed class EthernetTask : Task
        {
            public EthernetTask(AltoCPU cpu) : base(cpu)
            {
                _taskType = TaskType.Ethernet;                
                _wakeup = false;

                _ethernetController = _cpu._system.EthernetController;
            }

            protected override bool ExecuteInstruction(MicroInstruction instruction)
            {                
                // The Ethernet task only remains awake if there are pending data wakeups                
                if (_ethernetController.CountdownWakeup)
                {
                    //
                    // The resulting [Countdown] wakeup is cleared when the Ether task next runs.                        
                    _ethernetController.CountdownWakeup = false;
                    _wakeup = false;
                }                                      

                return base.ExecuteInstruction(instruction);
            }            

            protected override ushort GetBusSource(int bs)
            {
                EthernetBusSource ebs = (EthernetBusSource)bs;

                switch(ebs)
                {
                    case EthernetBusSource.EIDFCT:
                        // Input Data Function. Gates the contents of the FIFO to BUS[0-15], and
                        // increments the read pointer at the end of the cycle.
                        return _ethernetController.ReadInputFifo(false /* increment read pointer */);

                    default:
                        throw new NotImplementedException(String.Format("Unimplemented Ethernet BS {0}", ebs));
                }
            }

            protected override void ExecuteSpecialFunction1(MicroInstruction instruction)
            {
                EthernetF1 ef1 = (EthernetF1)instruction.F1;
                switch(ef1)
                {
                    case EthernetF1.EILFCT:
                        // Input Look Function. Gates the contents of the FIFO to BUS[0-15] but does
                        // not increment the read pointer.
                        _busData &= _ethernetController.ReadInputFifo(true /* do not increment read pointer */);
                        break;

                    case EthernetF1.EPFCT:
                        // Post Function. Gates interface status to BUS[8-15]. Resets the interface at
                        // the end of the cycle and removes wakeup for this task.
                        _busData &= _ethernetController.Status;
                        _ethernetController.ResetInterface();
                        _wakeup = false;
                        Log.Write(LogComponent.EthernetController, "EPFCT: Status {0}, bus now {1}",
                            Conversion.ToOctal(_ethernetController.Status),
                            Conversion.ToOctal(_busData));
                        break;

                    case EthernetF1.EWFCT:
                        // Countdown Wakeup Function. Sets a flip flop in the interface that will
                        // cause a wakeup to the Ether task on the next tick of SWAKMRT. This
                        // function must be issued in the instruction after a TASK. The resulting
                        // wakeup is cleared when the Ether task next runs.      
                        Log.Write(LogComponent.EthernetController, "Enabling countdown wakeups.");
                        _ethernetController.CountdownWakeup = true;
                        break;                    

                    default:
                        throw new NotImplementedException(String.Format("Unimplemented Ethernet F1 {0}", ef1));

                }
            }

            protected override void ExecuteSpecialFunction2(MicroInstruction instruction)
            {
                EthernetF2 ef2 = (EthernetF2)instruction.F2;
                switch (ef2)
                {
                    case EthernetF2.EODFCT:
                        // Output Data Function. Loads the FIFO from BUS[0-15], then increments the
                        // write pointer at the end of the cycle.
                        _ethernetController.WriteOutputFifo(_busData);
                        break;

                    case EthernetF2.EOSFCT:
                        // Output Start Function. Sets the OBusy flip flop in the interface, starting
                        // data wakeups to fill the FIFO for output. When the FIFO is full, or EEFct has
                        // been issued, the interface will wait for silence on the Ether and begin
                        // transmitting.
                        _ethernetController.StartOutput();
                        break;

                    case EthernetF2.ERBFCT:
                        // Reset Branch Funct ion. This command dispatch function merges the ICMD
                        // and OCMD flip flops, into NEXT[6-7]. These flip flops are the means of
                        // communication between the emulator task and the Ethernet task. The
                        // emulator task sets them from BUS[14-15] with the STARTF function, causing
                        // the Ethernet task to wakeup, dispatch on them and then reset them with
                        // EPFCT.
                        Log.Write(LogComponent.EthernetController, "EBRBFCT: SIO is {0}.", _ethernetController.IOCMD);
                        _nextModifier |= (ushort)((_ethernetController.IOCMD << 2));
                        break;

                    case EthernetF2.EEFCT:
                        // End of transmission Function. This function is issued when all of the main
                        // memory output buffer has been transferred to the FIFO. EEFCT disables
                        // further data wakeups.
                        _ethernetController.EndTransmission();
                        break;

                    case EthernetF2.EBFCT:
                        // Branch Function. ORs a one into NEXT[7] if an input data late is detected,
                        // or an SIO with AC0[14:15] non-zero is issued, or if the transmitter or receiver
                        // goes done. ORs a one into NEXT[6] if a collision is detected.
                        
                        if (_ethernetController.DataLate || 
                            _ethernetController.IOCMD != 0 ||
                            _ethernetController.OperationDone)
                        {
                            Log.Write(LogComponent.EthernetController, "EBFCT: DataLate {0} IOCMD {1} Done {2}", _ethernetController.DataLate, _ethernetController.IOCMD, _ethernetController.OperationDone);
                            _nextModifier |= 0x4;
                        }

                        if (_ethernetController.Collision)
                        {
                            Log.Write(LogComponent.EthernetController, "EBFCT: Collision");
                            _nextModifier |= 0x8;
                        }
                        break;

                    case EthernetF2.ECBFCT:
                        // Countdown Branch Function. ORs a one into NEXT[7] if the FIFO is not
                        // empty.
                        if (!_ethernetController.FIFOEmpty)
                        {
                            Log.Write(LogComponent.EthernetController, "ECBFCT: FIFO empty");
                            _nextModifier |= 0x4;
                        }
                        break;

                    case EthernetF2.EISFCT:
                        // Input Start Function. Sets the IBusy flip flop in the interface, causing it to
                        // hunt for the beginning of a packet: silence on the Ether followed by a
                        // transition. When the interface has collected two words, it will begin
                        // generating data wakeups to the microcode.
                        _ethernetController.StartInput();
                        break;

                    default:
                        throw new NotImplementedException(String.Format("Unimplemented Ethernet F2 {0}", ef2));

                }
            }

            private EthernetController _ethernetController;
        }        
    }
}
