package com.lemenzo.contacts;

import android.app.*;
import android.os.*;
import android.view.*;
import android.widget.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;

public class CsvImportActivity extends Activity {
    private String[] headers;
    private int contactCount;
    private final List<Spinner> maps=new ArrayList<>();
    private ContactsRepository repo;
    private Button importButton;
    private final ExecutorService io=Executors.newSingleThreadExecutor(r->new Thread(r,"LemenzoContacts-CSV"));
    private final Handler main=new Handler(Looper.getMainLooper());
    private volatile boolean destroyed;

    public void onCreate(Bundle b){
        super.onCreate(b);
        repo=new ContactsRepository(this);
        LinearLayout loading=new LinearLayout(this);
        loading.setGravity(Gravity.CENTER);
        loading.setBackgroundColor(Ui.BG);
        loading.addView(Ui.text(this,getString(R.string.csv_mapping),16,Ui.SECONDARY));
        setContentView(loading);

        io.execute(()->{
            try(InputStream in=getContentResolver().openInputStream(getIntent().getData())){
                if(in==null)throw new IOException("Unable to open CSV");
                final String[][] first={null}; final int[] count={0};
                ContactFileCodec.readCsvStreaming(in,(row,index)->{if(index==0)first[0]=row;else count[0]++;});
                if(first[0]==null)throw new IOException("Empty CSV");
                main.post(()->{
                    if(destroyed)return;
                    headers=first[0];contactCount=count[0];build();
                });
            }catch(Exception e){
                main.post(()->{
                    if(destroyed)return;
                    Toast.makeText(this,R.string.import_failed,Toast.LENGTH_LONG).show();
                    finish();
                });
            }
        });
    }

    private void build(){
        LinearLayout root=new LinearLayout(this);root.setOrientation(LinearLayout.VERTICAL);
        root.setBackgroundColor(Ui.BG);Ui.applySafeInsets(this,root);
        root.addView(Ui.subHeader(this,getString(R.string.csv_mapping)));
        ScrollView sv=new ScrollView(this);
        LinearLayout box=new LinearLayout(this);box.setOrientation(LinearLayout.VERTICAL);
        box.setPadding(Ui.dp(this,16),0,Ui.dp(this,16),Ui.dp(this,24));
        box.addView(Ui.text(this,getString(R.string.detected_contacts,contactCount),14,Ui.SECONDARY));
        box.addView(Ui.space(this,12));

        String[] labels={getString(R.string.ignore),getString(R.string.first_name),
                getString(R.string.middle_name),getString(R.string.last_name),getString(R.string.display_name),
                getString(R.string.phone),getString(R.string.email),getString(R.string.company),
                getString(R.string.job_title),getString(R.string.address),getString(R.string.birthday),
                getString(R.string.notes)};
        maps.clear();
        for(String h:headers){
            LinearLayout card=Ui.card(this);card.addView(Ui.title(this,h,14));
            Spinner sp=new Spinner(this);
            ArrayAdapter<String>a=new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,labels){
                public View getView(int p,View v,ViewGroup parent){
                    TextView t=Ui.text(CsvImportActivity.this,getItem(p),14,Ui.TEXT);
                    t.setPadding(Ui.dp(CsvImportActivity.this,14),0,Ui.dp(CsvImportActivity.this,14),0);
                    t.setBackground(Ui.outline(CsvImportActivity.this,Ui.SURFACE2,Ui.OUTLINE,14));return t;
                }
                public View getDropDownView(int p,View v,ViewGroup parent){
                    TextView t=Ui.text(CsvImportActivity.this,getItem(p),14,Ui.TEXT);
                    t.setPadding(Ui.dp(CsvImportActivity.this,16),Ui.dp(CsvImportActivity.this,12),
                            Ui.dp(CsvImportActivity.this,16),Ui.dp(CsvImportActivity.this,12));
                    t.setBackgroundColor(Ui.SURFACE);return t;
                }
            };
            sp.setAdapter(a);sp.setSelection(guess(h));card.addView(sp);maps.add(sp);
            box.addView(card);box.addView(Ui.space(this,8));
        }
        LinearLayout preview=Ui.raisedCard(this);
        preview.addView(Ui.section(this,getString(R.string.preview)));preview.addView(Ui.space(this,5));
        preview.addView(Ui.text(this,getString(R.string.preview_contacts,contactCount),14,Ui.TEXT));
        box.addView(preview);box.addView(Ui.space(this,12));

        importButton=Ui.button(this,getString(R.string.import_contacts)+" ("+contactCount+")",true);
        importButton.setOnClickListener(v->doImport());
        box.addView(importButton);sv.addView(box);
        root.addView(sv,new LinearLayout.LayoutParams(-1,0,1));
        setContentView(root);
    }

    private int guess(String h){
        String s=h.toLowerCase(Locale.ROOT).replace("_"," ").replace("-"," ");
        if(s.contains("first")||s.contains("given"))return 1;if(s.contains("middle"))return 2;
        if(s.contains("last")||s.contains("surname")||s.contains("family"))return 3;
        if(s.contains("display")||s.equals("name"))return 4;if(s.contains("phone")||s.contains("mobile")||s.contains("tel"))return 5;
        if(s.contains("mail"))return 6;if(s.contains("company")||s.contains("organization"))return 7;
        if(s.contains("title")||s.contains("job"))return 8;if(s.contains("address")||s.contains("street"))return 9;
        if(s.contains("birth"))return 10;if(s.contains("note"))return 11;return 0;
    }

    private void doImport(){
        if(importButton!=null)importButton.setEnabled(false);
        final int[] mapping=new int[maps.size()];
        for(int i=0;i<maps.size();i++)mapping[i]=maps.get(i).getSelectedItemPosition();

        io.execute(()->{
            final int[] ok={0},skip={0},fail={0};
            final ArrayList<ContactData> batch=new ArrayList<>(80);
            try(InputStream in=getContentResolver().openInputStream(getIntent().getData())){
                if(in==null)throw new IOException("Unable to reopen CSV");
                ContactFileCodec.readCsvStreaming(in,(row,index)->{
                    if(index==0)return;
                    try{
                        ContactData d=fromRow(row,mapping);
                        if(d.displayName.isEmpty()&&d.phones.isEmpty()&&d.emails.isEmpty()){skip[0]++;return;}
                        batch.add(d);
                        if(batch.size()>=80){
                            int done=repo.importContacts(batch);ok[0]+=done;fail[0]+=batch.size()-done;batch.clear();
                        }
                    }catch(Exception e){fail[0]++;}
                });
                if(!batch.isEmpty()){int done=repo.importContacts(batch);ok[0]+=done;fail[0]+=batch.size()-done;batch.clear();}
            }catch(Exception e){fail[0]++;}
            main.post(()->{
                if(destroyed)return;
                Toast.makeText(this,getString(R.string.import_result,ok[0],skip[0],fail[0]),Toast.LENGTH_LONG).show();
                finish();
            });
        });
    }

    private ContactData fromRow(String[] row,int[] mapping){
        ContactData d=new ContactData();
        for(int i=0;i<mapping.length&&i<row.length;i++){
            int f=mapping[i];String v=row[i].trim();if(v.isEmpty())continue;
            switch(f){
                case 1:d.firstName=v;break;case 2:d.middleName=v;break;case 3:d.lastName=v;break;
                case 4:d.displayName=v;break;
                case 5:for(String x:v.split("\\s*\\|\\s*"))if(!x.isEmpty()){d.phones.add(x);d.phoneTypes.add(android.provider.ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);}break;
                case 6:for(String x:v.split("\\s*\\|\\s*"))if(!x.isEmpty()){d.emails.add(x);d.emailTypes.add(android.provider.ContactsContract.CommonDataKinds.Email.TYPE_HOME);}break;
                case 7:d.company=v;break;case 8:d.jobTitle=v;break;case 9:d.address=v;break;
                case 10:d.birthday=v;break;case 11:d.notes=v;break;
            }
        }
        if(d.displayName.isEmpty())d.displayName=(d.firstName+" "+d.lastName).trim();
        return d;
    }

    @Override protected void onDestroy(){
        destroyed=true;io.shutdownNow();super.onDestroy();
    }
}
