using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToTwitter;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Windows.Forms;

namespace Azyobuzi.Twirunrun
{
    //https://gist.github.com/835953
    public class EncryptedFileAuthorizer
        : OAuthAuthorizer,
          ITwitterAuthorizer
    {
        private static readonly Byte[] _entropy = "twirunrun_access_token_file_key".ToBytes();

        private readonly FileInfo _tokenFile;

        public String AccountId
        {
            get;
            protected set;
        }

        private string GetPin(Uri uri)
        {
            Process.Start(uri.ToString());
            using (var f = new PinForm())
            {
                f.Text = "Twitter認証";
                f.TopMost = true;
                f.DescriptionLbl.Text = "Twitterの認証画面で表示される暗証番号を入力してください";
                f.ShowDialog();
                return f.InputText;
            }
        }

        public EncryptedFileAuthorizer()
        {
            this._tokenFile = new FileInfo(Application.StartupPath + @"\settings\AccessToken.dat");
            this.Credentials = new InMemoryCredentials()
            {
                ConsumerKey = AppSettings.ConsumerKey,
                ConsumerSecret = AppSettings.ConsumerSecret,
            };
            this.OAuthTwitter = new OAuthTwitter()
            {
                OAuthConsumerKey = this.Credentials.ConsumerKey,
                OAuthConsumerSecret = this.Credentials.ConsumerSecret,
            };
        }

        public void Authorize()
        {
            if (!this.IsAuthorized)
            {
                this.Load();
                if (!this.IsAuthorized)
                {
                    String screenName;
                    String userID;
                    String link = OAuthTwitter.AuthorizationLinkGet(this.OAuthRequestTokenUrl, this.OAuthAuthorizeUrl, "oob", false);
                    String verifier = this.GetPin(new Uri(link));
                    String oAuthToken = new Uri(link).Query.TrimStart('?')
                        .Split('&')
                        .Select(s => s.Split('='))
                        .Where(_ => _[0] == "oauth_token")
                        .Select(_ => _[1])
                        .SingleOrDefault();
                    this.OAuthTwitter.AccessTokenGet(oAuthToken, verifier, this.OAuthAccessTokenUrl, "", out screenName, out userID);
                    this.ScreenName = screenName;
                    this.UserId = userID;
                    this.Credentials.OAuthToken = this.OAuthTwitter.OAuthToken;
                    this.Credentials.AccessToken = this.OAuthTwitter.OAuthTokenSecret;
                    using (SHA1Cng sha1 = new SHA1Cng())
                    {
                        this.AccountId = String.Join("", sha1.ComputeHash(Encoding.BigEndianUnicode.GetBytes("!Id=" + userID + "@com.twitter"))
                            .Select(b => b.ToString("x2")));
                    }
                    this.Save();
                }
            }
        }

        public void Load()
        {
            if (this._tokenFile.Exists)
            {
                try
                {
                    String[] data = Encoding.UTF32.GetString(ProtectedData.Unprotect(
                        File.ReadAllBytes(this._tokenFile.FullName),
                        _entropy,
                        DataProtectionScope.LocalMachine
                    ))
                        .Split(
                            new String[] { "\0\0\0\0" },
                            5,
                            StringSplitOptions.None
                        );
                    this.Credentials.OAuthToken = data[0];
                    this.Credentials.AccessToken = data[1];
                    this.UserId = data[2];
                    this.ScreenName = data[3];
                    this.AccountId = data[4];
                }
                catch (Exception)
                {
                    this.Credentials.OAuthToken = null;
                    this.Credentials.AccessToken = null;
                    this.UserId = null;
                    this.ScreenName = null;
                    this.AccountId = null;
                }
                finally
                {
                    this.OAuthTwitter.OAuthToken = this.Credentials.OAuthToken;
                    this.OAuthTwitter.OAuthTokenSecret = this.Credentials.AccessToken;
                }
            }
        }

        public void Save()
        {
            if (this.IsAuthorized)
            {
                File.WriteAllBytes(this._tokenFile.FullName, ProtectedData.Protect(
                    Encoding.UTF32.GetBytes(String.Join(
                        "\0\0\0\0",
                        new String[]
                        {
                            this.Credentials.OAuthToken,
                            this.Credentials.AccessToken,
                            this.UserId,
                            this.ScreenName,
                            this.AccountId,
                        }
                    )),
                    _entropy,
                    DataProtectionScope.LocalMachine
                ));
            }
        }

        public void Load(String credentialsString)
        {
            String[] credentials = credentialsString.Split(',');
            this.Credentials.OAuthToken = credentials[0];
            this.Credentials.AccessToken = credentials[1];
        }
    }
}
