﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using lib8081.hardware;
using lib8081.hardware.devices;
using lib8081.instructions;

namespace lib8081
{
    public class CPU
    {
	    private Dictionary<string, lib8081.instructions.Function> functions;

	    public Dictionary<string, lib8081.instructions.Function> GetFunctions()
	    {
		    return functions;
	    }

	    public void SetFunctions(Dictionary<string, lib8081.instructions.Function> d)
	    {
		    this.functions = d;
	    }

	    // Global registers and stack.
        public lib8081.hardware.Register<Int16> in1 = new Register<Int16>("in1", 32);
        public lib8081.hardware.Register<Int16> in2 = new Register<Int16>("in2", 32);
        public lib8081.hardware.Register<Int32> in3 = new Register<Int32>("in3", 64);
        public lib8081.hardware.Register<Double> fp1 = new Register<Double>("fp1", 32);
        public lib8081.hardware.Register<double> fp2 = new Register<Double>("fp2", 64);
        public lib8081.hardware.Register<byte> bb1 = new Register<byte>("bb1", 128);

        public Stack<Object> stack = new Stack<object>(16);      

		// Hardware objects.
	    private Dictionary<String, lib8081.hardware.devices.IDevice> devices;

	    internal CPU()
	    {
			this.devices = new Dictionary<string, IDevice>();    
	    }


	    public void AddDevice(String name, IDevice device)
	    {
		    devices[name] = device;
	    }

	    public IDevice GetDevice(String name)
	    {
		    return devices[name];
	    }

	    public void PrintData()
	    {
		    foreach (KeyValuePair<String, Function> pair in functions)
		    {
			    Console.WriteLine(String.Format("Name: {0} Data: {1}", pair.Key, pair.Value));
			    foreach (Instruction ipair in pair.Value.GetInstructions())
			    {
					Console.WriteLine(String.Format("Instruction: {0}", ipair));   
			    }
		    }
	    }

	    public Boolean Run()
	    {
		    // Get the main entry point.
		    Function main = null;
		    this.functions.TryGetValue("main", out main);
		    if (main == null)
		    {
			    throw new Exception("Unable to find entry point `main`.");
		    }
		    main.call(null);
		    return true;

	    }
    }
}
