public SpotifyConnection(ICache cache, TimeSpan timeout)
 {
     this.session = new Session();
     this.protocol = null;
     this.running = false;
     this.user = null;
     this.userSemaphore = new Semaphore(0, 2);
     this.cache = cache;
     this.timeout = timeout;
 }
        /// <summary>
        /// Login to Spotify using the specified username and password.
        /// </summary>
        /// <param name="username">Username to use.</param>
        /// <param name="password">Corresponding password.</param>
        public void Login(string username, string password)
        {
            if (this.protocol != null)
            {
                throw new InvalidOperationException("Already logged in!");
            }
            this.protocol = this.session.Authenticate(username, password);
            this.user = new Sharpotify.Media.User(username);
            this.protocol.AddListener(this);

            /*Start the thread*/
            /*It only runs one time, then it stops. The first asyncronous call is needed :)*/
            System.Threading.Thread backgroundProcess = new System.Threading.Thread(delegate() { this.Run(); });
            backgroundProcess.Name = "Sharpotify.Background";
            backgroundProcess.IsBackground = true;
            backgroundProcess.Start();
        }
        public void CommandReceived(int command, byte[] payload)
        {
            byte[] aux;
            switch (command)
            {
                case Command.COMMAND_SECRETBLK:
                    /* Check length. */
                    if (payload.Length != 336)
                    {
                        System.Console.WriteLine("Got command 0x02 with len " + payload.Length  + ", expected 336!\n");
                    }

                    /* Check RSA public key. */
                    byte[] rsaPublicKey = this.session.RSAPublicKey.ToByteArray();

                    for (int i = 0; i < 128; i++)
                    {
                        if (payload[16 + i] != rsaPublicKey[i])
                        {
                            System.Console.WriteLine("RSA public key doesn't match! " + i + "\n");

                            break;
                        }
                    }

                    /* Send cache hash. */
                    try
                    {
                        this.protocol.SendCacheHash();
                    }
                    catch (ProtocolException)
                    {
                        /* Just don't care. */
                    }

                    break;

                case Command.COMMAND_PING:
                    /* Ignore the timestamp but respond to the request. */
                    /* int timestamp = IntegerUtilities.bytesToInteger(payload); */
                    try
                    {
                        this.protocol.SendPong();
                    }
                    catch(ProtocolException)
                    {
                        /* Just don't care. */
                    }

                    break;

                case Command.COMMAND_PONGACK:
                    break;

                case Command.COMMAND_CHANNELDATA:
                    Channel.Process(payload);
                    break;

                case Command.COMMAND_CHANNELERR:
                    Channel.Error(payload);
                    break;

                case Command.COMMAND_AESKEY:
                    aux = new byte[payload.Length - 2];
                    Array.Copy(payload, 2, aux, 0, aux.Length);
                    /* Channel id is at offset 2. AES Key is at offset 4. */
                    Channel.Process(aux);
                    break;

                case Command.COMMAND_AESKEYERR:
                    aux = new byte[payload.Length - 2];
                    Array.Copy(payload, 2, aux, 0, aux.Length);
                    /* Channel id is at offset 2. */
                    Channel.Error(aux);
                    break;

                case Command.COMMAND_SHAHASH:
                    /* Do nothing. */
                    break;

                case Command.COMMAND_COUNTRYCODE:
                    //System.out.println("Country: " + new String(payload, Charset.forName("UTF-8")));
                    this.user.Country = new RegionInfo(Encoding.UTF8.GetString(payload));

                    /* Release 'country' permit. */
                    this.userSemaphore.Release();

                    break;

                case Command.COMMAND_P2P_INITBLK:
                    /* Do nothing. */
                    break;

                case Command.COMMAND_NOTIFY:
                    /* HTML-notification, shown in a yellow bar in the official client. */
                    /* Skip 11 byte header... */
                    /*System.out.println("Notification: " + new String(
                        Arrays.copyOfRange(payload, 11, payload.length), Charset.forName("UTF-8")
                    ));*/
                    aux = new byte[payload.Length - 11];
                    Array.Copy(payload, 11, aux, 0, payload.Length - 11);
                    this.user.Notification = Encoding.UTF8.GetString(aux);
                    break;

                case Command.COMMAND_PRODINFO:
                    this.user = XMLUserParser.ParseUser(payload, this.user);

                    /* Release 'prodinfo' permit. */
                    this.userSemaphore.Release();

                    break;

                case Command.COMMAND_WELCOME:
                    break;

                case Command.COMMAND_LICENSE:
                    break;

                case Command.COMMAND_PAUSE:
                    /* TODO: Show notification and pause. */
                    break;

                case Command.COMMAND_PLAYLISTCHANGED:
                    System.Console.WriteLine("Playlist '" + Hex.ToHex(payload) + "' changed!\n");

                    break;

                default:
                    System.Console.WriteLine("Unknown Command: 0x" + command.ToString("X2") + " Length: " + payload.Length + "\n");
                    System.Console.WriteLine("Data: " + Encoding.UTF8.GetString(payload) + " " + Hex.ToHex(payload));

                    break;
            }
        }