/*
 * Copyright (c) 2013, Psiphon Inc.
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package ca.psiphon.ploggy;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * User interface for adding friends.
 *
 * This activity is invoked by various sources of serialized Identity blobs,
 * including .ploggy files (e.g., Email attachments), NFC messages, etc.
 */
public class ActivityAddFriend extends ActivitySendIdentityByNfc implements View.OnClickListener {

    public static final String IDENTITY_LINK_PREFIX = "ftp://identity.ploggy/#";

    private static final String LOG_TAG = "Add Friend";

    private ImageView mFriendAvatarImage;
    private TextView mFriendNicknameText;
    private TextView mFriendFingerprintText;
    private Button mFriendAddButton;

    protected Data.Friend mReceivedFriend;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_friend);

        mFriendAvatarImage = (ImageView)findViewById(R.id.add_friend_friend_avatar_image);
        mFriendNicknameText = (TextView)findViewById(R.id.add_friend_friend_nickname_text);
        mFriendFingerprintText = (TextView)findViewById(R.id.add_friend_friend_fingerprint_text);
        mFriendAddButton = (Button)findViewById(R.id.add_friend_add_button);
        mFriendAddButton.setOnClickListener(this);
        mReceivedFriend = null;
    }

    protected void showFriend() {
        if (mReceivedFriend != null) {
            try {
                Robohash.setRobohashImage(this, mFriendAvatarImage, true, mReceivedFriend.mPublicIdentity);
                mFriendNicknameText.setText(mReceivedFriend.mPublicIdentity.mNickname);
                mFriendFingerprintText.setText(Utils.formatFingerprint(mReceivedFriend.mPublicIdentity.getFingerprint()));
                return;
            } catch (Utils.ApplicationError e) {
                Log.addEntry(LOG_TAG, "failed to show friend");
            }
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        setIntent(intent);
    }

    private void setupForegroundDispatch() {
        NfcAdapter nfcAdapter = getNfcAdapter();
        if (nfcAdapter != null) {
            IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
            try {
                intentFilter.addDataType(getNfcMimeType());
            } catch (IntentFilter.MalformedMimeTypeException e) {
                Log.addEntry(LOG_TAG, e.getMessage());
                return;
            }
            nfcAdapter.enableForegroundDispatch(
                   this,
                   PendingIntent.getActivity(
                           this,
                           0,
                           new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0),
                   new IntentFilter[] {intentFilter},
                   null);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        // TODO: ActivityGenerateSelf.checkLaunchGenerateSelf(this);?

        // TODO: foreground dispatch causes onResume to be called?
        Intent intent = getIntent();
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] ndefMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            NdefMessage ndefMessage = (NdefMessage)ndefMessages[0];
            String payload = new String(ndefMessage.getRecords()[0].getPayload());
            try {
                Identity.PublicIdentity publicIdentity = Json.fromJson(payload, Identity.PublicIdentity.class);
                Protocol.validatePublicIdentity(publicIdentity);
                Data.Friend friend = new Data.Friend(publicIdentity, new Date());
                // TODO: display validation error?
                mReceivedFriend = friend;
                showFriend();
                // TODO: update add_friend_description_text as well?
            } catch (Utils.ApplicationError e) {
                Log.addEntry(LOG_TAG, "failed to handle inbound NFC message");
            }
        } else {
            // Extract friend public identity from email attachment (or file)
            InputStream inputStream = null;
            try {
                Uri uri = intent.getData();

                if (uri.toString().startsWith(IDENTITY_LINK_PREFIX)) {
                    String fragment = uri.getFragment();
                    inputStream = new ByteArrayInputStream(fragment.getBytes("UTF-8"));
                }
                else {
                    String scheme = uri.getScheme();
                    if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
                        ContentResolver contentResolver = getContentResolver();
                        inputStream = contentResolver.openInputStream(uri);
                    } else {
                        String filePath = uri.getEncodedPath();
                        if (filePath != null) {
                            inputStream = new FileInputStream(new File(filePath));
                        }
                    }
                }

                if (inputStream != null) {
                    String payload = Utils.readInputStreamToString(inputStream);
                    Identity.PublicIdentity publicIdentity = Json.fromJson(payload, Identity.PublicIdentity.class);
                    Protocol.validatePublicIdentity(publicIdentity);
                    Data.Friend friend = new Data.Friend(publicIdentity, new Date());
                    // TODO: display validation error?
                    mReceivedFriend = friend;
                    showFriend();
                }
            } catch (IOException e) {
                Log.addEntry(LOG_TAG, e.getMessage());
                Log.addEntry(LOG_TAG, "failed to open .ploggy file");
            } catch (Utils.ApplicationError e) {
                Log.addEntry(LOG_TAG, "failed to open .ploggy file");
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
        // Use foreground dispatch to ensure repeated incoming beams don't create new activities
        setupForegroundDispatch();
    }

    @Override
    public void onPause() {
        super.onPause();
        NfcAdapter nfcAdapter = getNfcAdapter();
        if (nfcAdapter != null) {
            nfcAdapter.disableForegroundDispatch(this);
        }
    }

    @Override
    public void onClick(View view) {
        if (view.equals(mFriendAddButton)) {
            if (mReceivedFriend == null) {
                return;
            }
            try {
                Data.getInstance().addFriend(mReceivedFriend);
                String prompt = getString(R.string.prompt_add_friend_friend_added, mReceivedFriend.mPublicIdentity.mNickname);
                Toast.makeText(this, prompt, Toast.LENGTH_LONG).show();
                finish();
            } catch (Data.DataAlreadyExistsError e) {
                String prompt = getString(R.string.prompt_add_friend_friend_already_exists, mReceivedFriend.mPublicIdentity.mNickname);
                Toast.makeText(this, prompt, Toast.LENGTH_LONG).show();
            } catch (Utils.ApplicationError e) {
                Log.addEntry(LOG_TAG, "failed to add friend");
            }
        }
    }
}