using System;
using System.IO;
using System.Collections;
using System.Runtime.InteropServices;

namespace Karma {

    public delegate void OnUploadHandler(Song song);

    public class StorageDetails
    {
        public uint NumFiles;
        public ulong StorageSize;
        public ulong FreeSpace;
        public uint MaxFid;
    }

    public class Device {
        int rio_id;

        private ArrayList filesToAdd = new ArrayList();

        [DllImport ("karma")]
        private static extern int lk_karma_connect(string ipHostOrPath);

        [DllImport ("karma")]
        private static extern void lk_karma_load_database(int rio);

        [DllImport ("karma")]
        private static extern void lk_karma_use_smalldb();

        [DllImport ("karma")]
        private static extern void lk_karma_get_storage_details(int rio, 
                int storage_id, out uint n_files, out ulong s_size, 
                out ulong f_space, out uint highest_file_id);

        [DllImport ("karma")]
        private static extern IntPtr lk_properties_andOrSearch(int mode, IntPtr search_in, string key, string data);

        [DllImport ("karma")]
        private static extern int lk_rio_write(int rio, string filename);

        [DllImport ("karma")]
        private static extern int lk_karma_write_smalldb();

        [DllImport ("karma")]
        private static extern int lk_karma_delete_file(int rio, int fid);

        [DllImport ("karma")]
        private static extern int lk_karma_request_io_lock(int rio, int type);

        [DllImport ("karma")]
        private static extern int lk_karma_release_io_lock(int rio);

        public event OnUploadHandler ProgressChanged;

        public Device(string ipHostOrPath) {
            rio_id = lk_karma_connect(ipHostOrPath);
            if (rio_id < 0)
                    throw new ApplicationException("Failed to connect");

            lk_karma_use_smalldb();
            lk_karma_load_database(rio_id);
        }

        public StorageDetails GetStorageDetails()
        {
            uint nfiles;
            ulong size;
            ulong space;
            uint maxFid;

            lk_karma_get_storage_details(rio_id, 0, out nfiles, out size, 
                    out space, out maxFid);

            StorageDetails details = new StorageDetails();
            details.NumFiles = nfiles;
            details.StorageSize = size;
            details.FreeSpace = space;
            details.MaxFid = maxFid;

            return details;
        }

        public ICollection GetSongs() 
        {
            ArrayList songs = new ArrayList();

            IntPtr fids = lk_properties_andOrSearch(0, IntPtr.Zero, "fid", "");
            if (fids == IntPtr.Zero)
                return songs;

            int offset=0;
            int fid;
            while ((fid = Marshal.ReadInt32(fids, offset)) != 0) {
                Song tmp = new Song(fid);
                if (tmp.Type.Equals("tune"))
		            songs.Add(tmp);
                offset += 4;
            }
	        return songs;
        }

        public void QueueAddSong(string filename)
        {
            filesToAdd.Add(filename);
        }

        public void DeleteSong(int id)
        {
            lk_karma_request_io_lock(rio_id, 1);
            lk_karma_delete_file(rio_id, id);
            lk_karma_release_io_lock(rio_id);
        }

        public void Save()
        {
            lk_karma_request_io_lock(rio_id, 1);
            foreach (string filename in filesToAdd) {
                int fid = lk_rio_write(rio_id, filename);

                if (ProgressChanged != null && fid > 0) {
                    ProgressChanged(new Song(fid));
                }
            }
            filesToAdd.Clear();
            lk_karma_release_io_lock(rio_id);
            lk_karma_write_smalldb();
        }
    }
}
