package com.algobase.share.app;

import java.io.File;
import java.io.FileOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.Locale;
import java.util.List;
import java.util.ArrayList;

import java.util.regex.Pattern;

import android.app.Activity;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;

import androidx.core.content.FileProvider;

/*
import androidx.fragment.app.FragmentActivity;
*/


import android.view.Display;
import android.view.View;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.View.OnClickListener;

import android.widget.ProgressBar;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;

import android.graphics.Color;
import android.graphics.Point;

import android.net.Uri;

import android.os.Build;
import android.os.Handler;
import android.os.Environment;
import android.os.StatFs;

import android.util.Log;
import android.util.Patterns;

import android.app.Dialog;
import android.app.AlertDialog;

import android.content.DialogInterface;

/*
import android.preference.PreferenceManager;
*/

import android.accounts.AccountManager;
import android.accounts.Account;

import android.hardware.Sensor;
import android.hardware.SensorManager;

import com.algobase.share.system.*;
import com.algobase.share.network.*;
import com.algobase.share.compat.*;

import com.algobase.share.dialog.*;



public class SoftwareUpdate 
{
   String update_host  = "algobase.com";
   int    xserver_port = 9668;

   String package_name;
   String server_dir;
   String server_path;
   String app_name;
   String apk_name;

   String prefs_name = null;

   MyDialog update_dialog = null;

   TextView tv1 = null;
   TextView tv2 = null;
   ProgressBar  progress = null;
   CheckBox  cb1 = null;

   File download_folder;
   File apk_file;

   float local_version = 0;
   float remote_version = 0;

   String local_revision = "";
   String remote_revision = "";

   String local_build_time = "";
   String remote_build_time = "";

   String user_name = "unknown.user";

   int screen_width;
   int screen_height;
   float max_memory;
   float sd_free_gb;
   boolean baro = false;
   int ant = 0;

   String optional_args = "&ant=0";

   String error_str = null;

   boolean update_canceled = false;
   boolean update_denied = false;
   boolean update_started = false;

   boolean show_relnotes = false;


   //FragmentActivity activity = null;
   Activity activity = null;

   SharedPreferences prefs;

   Handler handler = new Handler();


   public void error_handler(String err) {}

   public void write_log(String msg) { if (msg != null) Log.v(app_name,msg); }



  String format(String pattern, Object... args)
   { String s = "";
     try { s = String.format(Locale.US,pattern,args);
     } catch (Exception e) {}
     return s;
   }


  String versionToString(float x)
   { String s = format("%.3f",x);
     if (s.endsWith("0")) s = s.substring(0,s.length()-1);
     return s;
    }


  void showToast(final String txt) {
     handler.post(new Runnable() {
        public void run() {
           Toast.makeText(activity,txt, Toast.LENGTH_SHORT).show();
        }
     });
   }

  void acknowledge(String txt)
  { final MyDialog diag = new MyDialog(activity);
    diag.setTitle("Program Update");
    diag.setMessage(txt);
    diag.setPositiveButton("ok", new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int which) {
          }
    });

    handler.post( new Runnable() { 
        public void run() { diag.show(); }
    });

   }


  public String getError() { return error_str; }


  int DipToPixels(float dp)
  { float dpi = activity.getResources().getDisplayMetrics().densityDpi;
    return (int)(0.5 + dp * (dpi/160f));
   }


  String xget_string(String path)
  { XClient xc = new XClient(update_host,xserver_port);
    return xc.getFile(path);
   }


  boolean xget_file(File file, String path)
  {  
    progress.setProgress(0);

    XClient xc = new XClient(update_host,xserver_port) {
      
    	  @Override
    	  public void progress_init(final int total){ 
              handler.post(new Runnable() {
                 public void run() {
                   progress.setMax(total); 
                   progress.setProgress(0); 
                 }
              });
          } 

    	  @Override
    	  public void progress(final int x,int total) { 
              progress.setProgress(x); 
              handler.post(new Runnable() {
                 public void run() {
                   int kb = x/1024;
                   tv2.setText(format("%d kb",kb));
                 }
              });
          }
      };


   return xc.getFile(path,file);
 }
    



  //public SoftwareUpdate(FragmentActivity act, File app_folder, 
  public SoftwareUpdate(Activity act, File app_folder, 
                                              String dir,
                                              String name, 
                                              float version)
  { 
    activity = act;

    package_name = activity.getPackageName();
    download_folder = app_folder;
    app_name = name;
    apk_name = name + ".apk";

    write_log("");
    write_log("SoftwareUpdate");

    local_version = version;

    server_dir = dir;
    server_path = "www/android/" + dir;

    WindowManager wm = 
          (WindowManager)activity.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    Point pt = new Point();
    display.getSize(pt);
    screen_width = pt.x;
    screen_height = pt.y;

    Runtime rt = Runtime.getRuntime();
    max_memory = (float)rt.maxMemory()/(1024*1024);
    write_log(format("Max Memory : %5.1f MB",max_memory));

    //prefs =  PreferenceManager.getDefaultSharedPreferences(activity);

    SensorManager sm = 
          (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
    baro = (sm.getDefaultSensor(Sensor.TYPE_PRESSURE) != null);


    File root = activity.getFilesDir();
    StatFs stat = new StatFs(root.getPath());
    float free_bytes = stat.getAvailableBlocksLong()*stat.getBlockSizeLong();
    sd_free_gb = free_bytes/(1024*1024*1024);
    write_log(format("Free Space : %5.1f GB", sd_free_gb));


    apk_file = new File(download_folder,app_name + ".apk");

    write_log("apk_file: " + apk_file.getPath());


/*
  if (activity.checkCallingOrSelfPermission("android.permission.GET_ACCOUNTS")
                                          == PackageManager.PERMISSION_GRANTED)
   {
     String account_name = null;

     Pattern emailPattern = Patterns.EMAIL_ADDRESS;
     Account[] accounts = AccountManager.get(activity).getAccounts();

     for(Account account:accounts) 
     { if (emailPattern.matcher(account.name).matches() && 
           (account.name.indexOf("gmail") != -1 ||
            account.name.indexOf("googlemail") != -1))
       { account_name = account.name;
         break;
        }
     }

     if (account_name != null)
     { String[] arr = account_name.split("@");
       user_name = arr[0];
      }
   }
*/


   }


  public void set_host(String host) { 
      update_host = host;
      remote_revision = "";
      remote_version = 0;
      remote_build_time = "";
  }

  public void set_update_dir(String dir) { 
    server_dir = dir;
    server_path = "www/android/" + dir;
  }

  public void set_args(String args) { 
      optional_args = args;
  }

  public void add_args(String args) { 
      optional_args += args;
  }


  public void set_prefs_name(String s) 
  { prefs_name = s; 
    prefs =  activity.getSharedPreferences(prefs_name,0);
   }

  public void set_version(float version) { 
           local_version = version; 
  }  

  public void set_svn_revision(String s) { 
           local_revision = s; 
  }  

  public void set_build_time(String s) { 
           local_build_time = s; 
  }  

  public void set_max_memory(float f) { max_memory = f; }
  public void set_user_name(String s) { user_name = s; }

  public String get_user_name() { return user_name; }

  public float getRemoteVersion() { 
      if (remote_version == 0) updateRemoteVersion();
      return remote_version; 
  }

  public String getRemoteRevision() { 
      if (remote_revision.equals("")) updateRemoteVersion();
      return remote_revision; 
  }

  public String getRemoteBuildTime() { 
      if (remote_build_time.equals("")) updateRemoteVersion();
      return remote_build_time; 
  }



   public void update_dialog(final boolean interactive, final float version)
   {  handler.post( new Runnable() { 
         public void run() { open_dialog(interactive,version); }
       });
    }
    

   void open_dialog(boolean interactive, final float version)
   {
     // version < 0 : developer version

     if (activity.isFinishing()) return;

      try { 
         if (activity.isDestroyed()) return;
      } catch (Exception e) {}

     if (version < 0)  update_denied = false;

     update_canceled = false;
     update_started = false;

     // do not ask again if denied
     if (update_denied) return;


     MyDialog dialog = new MyDialog(activity,"Software Update");

     dialog.setTitleTextSize(23);
     dialog.setMessageTextSize(18);

     if (version > 0)
       dialog.setMessage("Neue Version verfügbar.");
     else
     { dialog.setTitle(update_host);
       dialog.setMessage(server_dir + "/" + apk_name);
      }

     dialog.setAutoDismiss(false);

     tv1 = dialog.newTextView();
     tv1.setTextSize(17);
     dialog.addView(tv1);
     ((LayoutParams)tv1.getLayoutParams()).topMargin = DipToPixels(3);

     progress = dialog.newProgressBar();
     dialog.addView(progress);
     progress.getLayoutParams().height = DipToPixels(12);
     ((LayoutParams)progress.getLayoutParams()).rightMargin = DipToPixels(12);
     ((LayoutParams)progress.getLayoutParams()).topMargin = DipToPixels(3);

     tv2 = dialog.newTextView();
     tv2.setTextSize(15);
     tv2.setText("0 kb");
     dialog.addView(tv2);
     ((LayoutParams)tv2.getLayoutParams()).topMargin = DipToPixels(2);
     ((LayoutParams)tv2.getLayoutParams()).bottomMargin = DipToPixels(2);

     cb1 = dialog.newCheckBox();
     cb1.setText("   Release Notes");
     cb1.setChecked(version >= 0);
     dialog.addView(cb1);

     dialog.setViewPadding(10,0,10,0);


     if (interactive)
     dialog.setPositiveButton("Update",
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                if (update_started) return;
                show_relnotes = cb1.isChecked();
                update(show_relnotes,version);
            }
     });

     dialog.setNegativeButton("Cancel",
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                if (update_started)
                  update_canceled = true;
                else
                  update_denied = true;
                update_dialog.dismiss();
            }
     });

     String label = app_name;

     if (version >= 0) 
       label += "  " + versionToString(version);
     else
     if (remote_build_time.equals(""))
       label += "  (Developer)";
     else
       label = format("%.3f    %s",remote_version,remote_build_time);

     tv1.setText(label);


     dialog.show();

     update_dialog = dialog;

     if (!interactive)
     { cb1.setChecked(false);
       cb1.setVisibility(View.GONE);
       update(false,version);
      }
   }




    public boolean updateRemoteVersion()
    { 
      final String path = server_path;

      write_log("");
      write_log("updateRemoteVersion");
      write_log("update_host = " + update_host);
      write_log("path = " + path);

      long t = System.currentTimeMillis();
      String date_str = new SimpleDateFormat("yyyy-MM-dd").format(t);
      String time_str = new SimpleDateFormat("HH:mm:ss").format(t);
      String vers_str = versionToString(local_version);
      if (local_revision != null) vers_str += "-" + local_revision;

      String sys_str = "Android-" + Build.VERSION.RELEASE 
                                  + "/" + Build.VERSION.SDK_INT;

      String params = "";
      params += date_str;
      params += "  ";
      params += time_str;
      params += "  ";
      params += format("%-16s",user_name);
      params += " ";
      params += app_name;
      params += " ";
      params += format("%-12s",vers_str);
      params += " ";
      params += format("%-18s",sys_str);
      params += " ";
      params += format("%-12s",Build.MODEL);
      params += " ";
      params += format("%dx%d", screen_width, screen_height);
      params += " ";
      params += format("%4.0f MB", max_memory);
      params += " ";
      params += format("%5.1f GB", sd_free_gb);
      params += " ";
      if (baro) params += " baro";
      params += " ";
      params += optional_args.replaceAll("&"," ");


      // write update log

      final String txt = params;
      new MyThread() { 
    	public void run() { 
           XClient xc = new XClient(update_host,xserver_port);
           xc.appendString(txt, path + "/update.log");
        }
      }.start();


      // get remote version,revision, buildtime
/*
      String ver = xget_string(path + "/version.txt");
      String rev = xget_string(path + "/revision.txt");
      String bld = xget_string(path + "/buildtime.txt");

      if (ver == null)
      { write_log("software_update: connect failed.");
        return false;
       }

      try { remote_version = Float.parseFloat(ver);
      } catch (Exception e) { remote_version = 0; }

      remote_revision = (rev == null) ? "" : rev;
      remote_build_time = (bld == null) ? "" : bld;
*/

      XClient xc = new XClient(update_host,xserver_port);

      String[] files  = new String[] { path + "/version.txt",
                                       path + "/revision.txt",
                                       path + "/buildtime.txt" };
      String s = xc.getFiles(files);
    
      if (s == null)
      { write_log("software_update: connect failed.");
        return false;
       }

      String lines[] = s.split("\\s+");

      remote_version = 0;
      remote_revision = "";
      remote_build_time = "";

      try { remote_version = Float.parseFloat(lines[0]);
      } catch (Exception e) {}

      if (lines.length > 1) remote_revision   = lines[1];
      if (lines.length > 2) remote_build_time = lines[2];
      if (lines.length > 3) remote_build_time += " " + lines[3];

      write_log("remote_version = " + remote_version);
      write_log("remote_revision = " + remote_revision);
      write_log("remote_build_time = " + remote_build_time);


      return true;
    }
      
        
    void update(final boolean relnotes, final float version)
    { 
      // developer: version < 0

      write_log("program update: version = " + version);

      update_started = true;
      error_str = null;

      if (!download_folder.exists())
      { error_str  = "Cannot access download folder.";
        write_log(error_str);
        error_handler(error_str);
        if (update_dialog != null && update_dialog.isShowing()) 
           update_dialog.dismiss();
        return;
       }
 

      if (prefs != null)
      { SharedPreferences.Editor edit = prefs.edit();
        edit.putBoolean("release_notes",relnotes);
        edit.commit();
       }


      apk_name = app_name;
      if (version > 0) apk_name += "-" + versionToString(version);
      apk_name += ".apk";
       

      new MyThread() { 
    	public void run() { 

           String path = server_path;

           if (version > 0) path += "/versions";
           path += "/" + apk_name;

           if (!xget_file(apk_file, path)) {
              acknowledge("Cannot download " + path);
              return;
           }
             

           if (update_canceled) return;

           sleep(2000);

           if (update_canceled) return;

    	   update_dialog.dismiss();

           apk_file.setReadable(true,false);

           Uri uri = null;
           if (Build.VERSION.SDK_INT >= 24) 
           { // Android 7
             try {
               uri = FileProvider.getUriForFile(activity,package_name,apk_file);
             } catch(Exception ex) { acknowledge(ex.toString()); }
           }
           else
             uri = Uri.fromFile(apk_file);

           if (uri == null) return;

           //Intent intent = new Intent(Intent.ACTION_VIEW);
           Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
  
           intent.setDataAndType(uri,
                                "application/vnd.android.package-archive");

           intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK |
                           Intent.FLAG_ACTIVITY_NEW_TASK |
                           Intent.FLAG_GRANT_READ_URI_PERMISSION);

           activity.startActivity(intent);
        }

      }.start();
    }
   

    public boolean test_version()
    { updateRemoteVersion();
      return (remote_version > local_version + 0.001f);
     }


    public boolean test_revision()
    { updateRemoteVersion();
      int cmp = remote_revision.compareTo(local_revision);
      showToast("cmp revisions: " + cmp);
      return cmp > 0;
     }


    
    public void check()
    { 
      write_log("program_update: check");

      error_str = null;

      updateRemoteVersion();

      write_log("update.check: ");
      write_log(format("remote_version = %s", remote_version));
      write_log(format("local_version  = %s", local_version));
      write_log("");

      if (remote_version > local_version + 0.001f) {
        write_log("open update_dialog");
        update_dialog(true,remote_version);
      }

    }

}
