package com.xixun.xixunplayer;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.StrictMode;
import android.view.Choreographer;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.xixun.joey.aidlset.CardService;
import com.xixun.xy.conn.aidl.ConnService;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;

import gnph.util.Chsets;
import gnph.util.JSList;
import gnph.util.JSMap;

public class MainActivity extends Activity implements Choreographer.FrameCallback {

    public static MainActivity ins;
    ArrayList<BroadcastReceiver> reces = new ArrayList<>();
    ArrayList<ServiceConnection> services = new ArrayList<>();
    public Intent environIntent = new Intent();
    HashSet<IntentReceiver> environs = new HashSet<>();
    BackView backView;
    Prog progView, insView;
    long launchMilli = System.currentTimeMillis();
    long syncMs;
    int state;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ins = this;
        Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" Thread: "+Thread.currentThread().getId());

        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
        setContentView(backView = new BackView(MainActivity.this));
        state = 5;

        hookWebView();

        if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init();
        else {
            Util.println("---- No permission, Try again ...");
            Util.makeText(this, "No permission, Try again ...").show();
            ActivityCompat.requestPermissions(this, new String[]{
                android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                android.Manifest.permission.READ_EXTERNAL_STORAGE,
                android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
                android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
                android.Manifest.permission.INTERNET
            }, 999);
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(Util.programDir!=null) return;
        if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init();
        else {
            Util.println("---- Request Permission Failed");
            Util.makeText(this, "Request Permission Failed").show();
        }
    }
    @SuppressLint("SoonBlockedPrivateApi")
    public static void hookWebView() {
        if(android.os.Process.myUid() != android.os.Process.SYSTEM_UID) return;
        try {
            var factoryClass = Class.forName("android.webkit.WebViewFactory");
            var field = factoryClass.getDeclaredField("sProviderInstance");
            field.setAccessible(true);
            var sProviderInstance = field.get(null);
            if (sProviderInstance != null) {
                Util.println("sProviderInstance isn't null");
                return;
            }
            Method getProviderClassMethod;
            if (Build.VERSION.SDK_INT > 22) { // above 22
                getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
            } else if (Build.VERSION.SDK_INT == 22) { // method name is a little different
                getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            } else { // no security check below 22
                Util.println("Don't need to Hook WebView");
                return;
            }
            getProviderClassMethod.setAccessible(true);
            var providerClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
            var delegateClass = Class.forName("android.webkit.WebViewDelegate");
            var declaredConstructor = delegateClass.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            sProviderInstance = providerClass.getDeclaredMethod("create", delegateClass).invoke(providerClass, declaredConstructor.newInstance());
            field.set("sProviderInstance", sProviderInstance);
            Util.println("sProviderInstance"+sProviderInstance.toString());
            Util.println("Hook Done!");
        } catch (Throwable e) {
            Util.printStackTrace(e);
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Util.println("==<< MainActivity onDestroy <<<< this "+hashCode());
        state = 8;
        if(insView!=null) {
            insView.release();
            insView = null;
        }
        if(progView!=null) {
            progView.release();
            progView = null;
        }
        ins = null;
        for(var rece : reces) unregisterReceiver(rece);
        for(var service : services) unbindService(service);
        try (var fout = new FileOutputStream(Util.programDir+"/cfg")){
            Util.cfg.write(fout);
            fout.flush();
            fout.getFD().sync();
        } catch (Exception ignored) {
        }
        System.gc();
    }

    @Override
    protected void onStart() {
        super.onStart();
        Util.println("  ==>> MainActivity onStart >> "+hashCode());
    }
    @Override
    protected void onStop() {
        super.onStop();
        Util.println("  ==<< MainActivity onStop << "+hashCode());
    }
    @Override
    protected void onRestart() {
        super.onRestart();
        Util.println("  ==>> MainActivity onRestart >> "+hashCode());
    }

    @Override
    protected void onResume() {
        super.onResume();
        Util.println("  ==>> MainActivity onResume >> "+hashCode());
    }

    @Override
    protected void onPause() {
        super.onPause();
        Util.println("  ==<< MainActivity onPause << "+hashCode());
    }

    CardService serviCard;
    ConnService serviXy;
    Intent intenCard;
    ServiceConnection connCard;
    @SuppressLint("UnspecifiedRegisterReceiverFlag")
    public void init() {
        Util.initDir(this);
        if(MainService.ins==null) startService(new Intent(this, MainService.class));

        var cfg = new File(Util.programDir+"/cfg");
        if(! cfg.isFile()) Util.cfg = new JSMap();
        else {
            try {
                Util.cfg = JSMap.fromClose(new FileInputStream(cfg));
            } catch (Exception e) {
                Util.cfg = new JSMap();
                Util.makeText(MainActivity.this, Util.toStr(e)).show();
                Util.printStackTrace(e);
            }
        }
        Util.println(Util.cfg.toStr());
        Util.logOn = Util.cfg.get("logUploadUrl")!=null;
        Util.openHelper = new SQLiteOpenHelper(this, "aaa", null, 1) {
            @Override
            public void onCreate(SQLiteDatabase db) {
                db.execSQL("create table cfg(k varchar(32) primary key, v varchar(128))");
                db.execSQL("create table log(time integer primary key, dur integer, id varchar(64), name varchar(64), type varchar(16), lat double, lng double)");
            }
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            }
        };
        new LogThread().start();
        if(Util.logOn) {
            var db = Util.openHelper.getWritableDatabase();
            var cursor = db.rawQuery("select count(*) aaa from log", null);
            while(cursor.moveToNext()) Util.println("Log Cnt "+cursor.getString(0));
            db.close();
        }
        connCard = new ServiceConnection() {
            public void onServiceDisconnected(ComponentName name) {
                serviCard = null;
                Util.println("<-<- AIDL Service cardsystem Disconnected");
            }

            public void onServiceConnected(ComponentName name, IBinder iBinder) {
                unbindService(this);
                services.remove(this);
                Util.println("->-> AIDL Service cardsystem Connected");
                serviCard = CardService.Stub.asInterface(iBinder);
                try {
                    Util.isScreenOn = serviCard.isScreenOpen();
                    Util.screenWidth = serviCard.getScreenWidth();
                    Util.screenHeight = serviCard.getScreenHeight();
                    Util.println("  IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight);
                    backView.invalidate();
                    if(Util.isScreenOn) initProg();
                    else state = 8;
                } catch (Exception e) {
                    Util.makeText(MainActivity.this, Util.toStr(e)).show();
                    Util.printStackTrace(e);
                }
            }
        };
        intenCard = new Intent("com.xixun.joey.aidlset.SettingsService");
        intenCard.setPackage("com.xixun.joey.cardsystem");
        bindService(intenCard, connCard, Context.BIND_AUTO_CREATE);
        services.add(connCard);

        var connXy = new ServiceConnection() {
            public void onServiceDisconnected(ComponentName name) {
                serviXy = null;
                Util.println("<-<- AIDL Service xy.conn Disconnected");
            }
            public void onServiceConnected(ComponentName name, IBinder iBinder) {
                unbindService(this);
                services.remove(this);
                Util.println("->-> AIDL Service xy.conn Connected");
                serviXy = ConnService.Stub.asInterface(iBinder);
                try {
                    Util.serverURL = serviXy.getServerURL();
                    Util.println("  ServerURL: "+Util.serverURL);
                    if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/";
                    else {
                        if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL;
                        if(! Util.serverURL.endsWith("/")) Util.serverURL += "/";
                    }
                } catch (Exception e) {
                    Util.makeText(MainActivity.this, Util.toStr(e)).show();
                    Util.printStackTrace(e);
                }
            }
        };
        var intenXy = new Intent("xixun.intent.action.CONNECTION_INFO");
        intenXy.setPackage("com.xixun.xy.conn");
        bindService(intenXy, connXy, Context.BIND_AUTO_CREATE);
        services.add(connXy);

        reces.clear();
        BroadcastReceiver rece;
        registerReceiver(rece = new BroadcastReceiver(){
            @Override
            public void onReceive(Context context, Intent intent) {
                Util.println("Receive PAUSE_PLAYER");
                Util.isScreenOn = ! intent.getBooleanExtra("pause", false);
                Util.println("  IsScreenOn: "+Util.isScreenOn);
                if(! Util.isScreenOn) {
                    state = 8;
                    if(insView!=null) {
                        insView.release();
                        insView = null;
                    }
                    if(progView!=null) {
                        progView.release();
                        progView = null;
                    }
                    setContentView(backView);
                } else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg());
                System.gc();
            }
        }, new IntentFilter("com.xixun.action.PAUSE_PLAYER"));
        reces.add(rece);

        registerReceiver(rece = new BroadcastReceiver(){
            @Override
            public void onReceive(Context context, Intent intent) {
                Util.println("Receive CHANGE_COMPANYID");
                try {
                    if(serviXy!=null && serviXy.asBinder().isBinderAlive()) {
                        Util.serverURL = serviXy.getServerURL();
                        Util.println("  ServerURL: "+Util.serverURL);
                        if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/";
                        else {
                            if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL;
                            if(! Util.serverURL.endsWith("/")) Util.serverURL += "/";
                        }
                    } else {
                        Util.println("  bindService");
                        bindService(intenXy, connXy, Context.BIND_AUTO_CREATE);
                    }
                } catch (Exception e) {
                    Util.makeText(MainActivity.this, Util.toStr(e)).show();
                    Util.printStackTrace(e);
                }
            }
        }, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID"));
        reces.add(rece);

        registerReceiver(rece = new BroadcastReceiver(){
            @Override
            public void onReceive(Context context, Intent intent) {
                MainActivity.this.environIntent = intent;
                for(var environ : environs) {
                    environ.onReceive(intent);
                    ((View)environ).invalidate();
                }
            }
        }, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY"));
        reces.add(rece);

        registerReceiver(rece = new BroadcastReceiver(){
            @Override
            public void onReceive(Context context, Intent intent) {
                Util.lat = intent.getDoubleExtra("latitude", 0);
                Util.lng = intent.getDoubleExtra("longitude", 0);
                Util.println("  lat "+Util.lat+" lng "+Util.lng);
            }
        }, new IntentFilter("com.xixun.joey.gpsinfo"));
        reces.add(rece);

        registerReceiver(rece = new BroadcastReceiver(){
            @Override
            public void onReceive(Context context, Intent intent) {
                try {
                    var code = intent.getIntExtra("code", 0);
                    Util.println("  remote_control "+code);
                    if(progView == null || progView.pages.isEmpty() || ! progView.isShown() || code==-1 || code < -3 || code > progView.pages.size()) return;
                    if(code<=-2) {
                        var idx = avas.isEmpty() ? -1 : progView.pages.indexOf(avas.get(curAva));
                        if(code==-2) code = idx<=0 ? progView.pages.size() : idx;
                        else code = idx>=progView.pages.size()-1 ? 1 : idx+2;
                    }
                    var ms = System.currentTimeMillis();
                    if(! avas.isEmpty()) {
                        avas.get(curAva).hide();
                        if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) for(var call : progView.calls) call.doFrame(ms);
                    }
                    if(code==0) syncProg(ms, 0);
                    else {
                        avas.clear();
                        var page = progView.pages.get(code-1);
                        avas.add(page);
                        curAva = 0;
                        curTimes = 1;
                        waitTo = 0; //点播
                        page.setMillis(ms, ms);
                        if(state != 6) {
                            setContentView(progView);
                            state = 6;
                        }
                    }
                    var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program")));
                    root.put("Demand", code);
                    try (var fOut = new FileOutputStream(Util.programDir + "/program")) {
                        root.write(fOut);
                        fOut.flush();
                        fOut.getFD().sync();
                    }
                } catch (Throwable e) {
                    Util.makeText(MainActivity.this, Util.toStr(e)).show();
                    Util.printStackTrace(e);
                }
            }
        }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"));
        reces.add(rece);

        if(Util.custom==Util.Custom.Yishi) {
            new Thread(()->{
                while(true) {
                    try {
                        Thread.sleep(10000);
                        if(serviCard!=null && serviCard.asBinder().isBinderAlive()) {
                            var www = serviCard.getScreenWidth();
                            var hhh = serviCard.getScreenHeight();
                            if(www!=Util.screenWidth || hhh!=Util.screenHeight) {
                                var ins = MainActivity.ins;
                                if(ins!=null) ins.runOnUiThread(() -> {
                                    Util.screenWidth = www;
                                    Util.screenHeight = hhh;
                                    ins.backView.invalidate();
                                });
                            }
                        } else {
                            Util.println("  bindService");
                            var ins = MainActivity.ins;
                            if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE));
                        }
                    } catch (Throwable e) {
                        var ins = MainActivity.ins;
                        if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
                        Util.printStackTrace(e);
                    }
                }
            }).start();
        }
    }

    public void stopProg() {
        avas.clear();
        curAva = 0;
        curTimes = 1;
        if(insView!=null) {
            insView.release();
            insView = null;
        }
        if(progView!=null) {
            progView.release();
            progView = null;
        }
        setContentView(backView);
        System.gc();
    }
    public boolean delProgFile() {
        stopProg();
        var files = new File(Util.programDir).listFiles();
        var ok = true;
        if(files != null) for(var file : files) if(! file.delete()) ok = false;
        state = 4;
        try {
            var out = new FileOutputStream(Util.programDir+"/program");
            out.write("{}".getBytes());
            out.flush();
            out.getFD().sync();
            out.close();
        } catch (Throwable ignored) {
        }
        return ok;
    }
    public void initProg() {
        state = 1;
        try {
            Util.println("\nParse Insert Prog Json");
            var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/insert")));
            var task = root.jsmap("task");
            if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
            if(task!=null) {
                var view = new Prog(task, this);
                if(view.getChildCount()!=0) setContentView(insView = view);
            }
        } catch(FileNotFoundException ignored) {
        } catch(Throwable e) {
            state = 7;
            Util.printStackTrace(e);
        }
        var demand = 0;
        try {
            Util.println("\nParse Prog Json");
            var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program")));
            var task = root.jsmap("task");
            demand = root.intg("Demand");
            if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
            if(task==null) Util.println(root.isEmpty() ? "  Empty program JSON\n" : "  Error: task==null\n");
            else {
                var view = new Prog(task, this);
                if(view.getChildCount()==0) Util.println("  Error: ChildCount==0\n");
                else setContentView(progView = view);
            }
        } catch(FileNotFoundException e) {
            Util.println(""+e);
        } catch(Throwable e) {
            state = 7;
            Util.makeText(this, Util.toStr(e)).show();
            Util.printStackTrace(e);
        }
        if(insView!=null || progView!=null) {
            state = 5;
            Util.println("Init Sync");
            var ms = System.currentTimeMillis();
            if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0);
            else {
                avas.clear();
                var page = progView.pages.get(demand-1);
                avas.add(page);
                curAva = 0;
                curTimes = 1;
                waitTo = 0; //点播
                page.setMillis(ms, ms);
                if(state != 6) {
                    setContentView(progView);
                    state = 6;
                }
            }
            if(canAdd) {
                choreographer.postFrameCallback(this);
                canAdd = false;
            }
        } else if(state != 7) state = 3;
    }
    public void initProg(byte[] json) {
        try {
            Util.println("\nParse Prog Json");
            var root = JSMap.from(json);
            var task = root.jsmap("task");
            if(task==null) {
                if(! root.containsKey("layers")) {
                    state = 7;
                    Util.println("  Error: task==null\n");
                    Util.println(new String(json, Chsets.UTF8));
                    return;
                }
                task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
            }
            var view = new Prog(task, this);
            if(view.getChildCount()==0) {
                if(! view.isInsert) state = 7;
                Util.println("  Error: ChildCount==0\n");
                Util.println(new String(json, Chsets.UTF8));
                return;
            }
            if(Util.cfg.bool("needCheck")) {
                new Thread(()->{
                    if(Util.check(view)) runOnUiThread(() -> {
                        afterCheck(view, json);
                    });
                    else runOnUiThread(() -> {
                        view.release();
                        Util.makeText(this, "Contents are not passed").show();
                        Util.println("Contents are not passed");
                    });
                }).start();
            } else afterCheck(view, json);
        } catch (Throwable e) {
            state = 7;
            Util.makeText(this, Util.toStr(e)).show();
            Util.printStackTrace(e);
            Util.println(new String(json, Chsets.UTF8));
        }
    }
    public void afterCheck(Prog view, byte[] json) {
        try {
            if(view.isInsert) {
                if(insView!=null) insView.release();
                insView = view;
                try { avas.get(curAva).hide();} catch (Throwable ignored) {}
                setContentView(insView);
            } else {
                if(progView!=null) progView.release();
                progView = view;
                setContentView(progView);
            }
            var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program"));
            fOut.write(json);
            fOut.flush();
            fOut.getFD().sync();
            fOut.close();
            state = 5;
            System.gc();
            Util.println("Init Sync");
            syncProg((System.currentTimeMillis()+999)/1000*1000, 0);
            if(canAdd) {
                choreographer.postFrameCallback(this);
                canAdd = false;
            }
        } catch (Throwable e) {
            state = 7;
            Util.makeText(this, Util.toStr(e)).show();
            Util.printStackTrace(e);
            Util.println(new String(json, Chsets.UTF8));
        }
    }

    ArrayList<Prog.Page> avas = new ArrayList<>();
    int curAva, curTimes = 1;
    long waitTo = Long.MAX_VALUE;
    boolean isInsert;

    Choreographer choreographer = Choreographer.getInstance();
    boolean canAdd = true;

    @Override
    public void doFrame(long frameTimeNanos) {
        if(progView == null && insView==null) {
            canAdd = true;
            return;
        }
        choreographer.postFrameCallback(this);
        canAdd = false;
        var milli = System.currentTimeMillis();
        if(avas.isEmpty()) {
            if(milli >= waitTo) {
                waitTo = Long.MAX_VALUE;
                Util.println("wait sync");
                syncProg(milli, 0);
                if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
                if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
            }
            return;
        }
        var lastPage = avas.get(curAva);
        if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli);
        else {
            lastPage.hide();
            if(isInsert) {
                if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) {
                    var isDiff = milli-lastPage.endMilli>=1000;
                    syncProg(isDiff ? milli : lastPage.endMilli, milli);
                    if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
                    if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
                    return;
                }
            } else {
                if(waitTo > 0) {  //waitTo==0 为点播，不换页
                    if(curTimes < lastPage.repeatTimes) curTimes++;
                    else {
                        curTimes = 1;
                        if(++curAva >= avas.size()) {
                            var isDiff = milli-lastPage.endMilli>=1000;
                            syncProg(isDiff ? milli : lastPage.endMilli, milli);
                            if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
                            if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
                            return;
                        }
                    }
                } else if(milli-lastPage.endMilli >= 5000) {
                    Util.println("System Time Changed: "+Util.dateFmt.format(lastPage.endMilli)+" -> "+Util.dateFmt.format(milli));
                    lastPage.endMilli = milli;
                }
            }
            avas.get(curAva).setMillis(lastPage.endMilli, milli);
            Util.println("curAva: "+curAva+" endMs: "+avas.get(curAva).endMilli);
        }
        if(isInsert) for(var call : insView.calls) call.doFrame(milli);
        else for(var call : progView.calls) call.doFrame(milli);
    }

    void syncProg(long milli, long cur) {
        if(cur==0) cur = milli;
        Util.println("\nSyncProg");
        waitTo = Long.MAX_VALUE;
        avas.clear();
        curAva = 0;
        curTimes = 1;
        isInsert = false;
        if(insView!=null) {
            for(int i=0; i<insView.pages.size(); i++) if(insView.pages.get(i).repeatTimes <= 0) {
                for(var layer : insView.pages.get(i).layers) for(var src : layer.srcs) {
                    insView.removeView(src.view);
                    if(src.view instanceof Choreographer.FrameCallback) insView.calls.remove((Choreographer.FrameCallback) src.view);
                }
                insView.pages.remove(i--);
            }
            if(insView.pages.isEmpty()) {
                insView.release();
                insView = null;
                try {
                    var fOut = new FileOutputStream(Util.programDir + "/insert");
                    fOut.write("{}".getBytes());
                    fOut.flush();
                    fOut.getFD().sync();
                    fOut.close();
                } catch(Exception e) {
                    Util.printStackTrace(e);
                }
                System.gc();
            } else {
                var dur = 0;
                for(var page : insView.pages) if(page.isScheOn(milli+dur)) {
                    avas.add(page);
                    dur += page.tDur;
                }
                if(avas.isEmpty()) for(var page : insView.pages) if(page.sches==null) avas.add(page);
                if(! avas.isEmpty()) {
                    isInsert = true;
                    avas.get(curAva).setMillis(milli, cur);
                    if(! insView.isShown()) setContentView(insView);
                    state = 6;
                    Util.println("avas "+avas.size()+" Insert");
                    return;
                }
            }
        }
        if(progView!=null) {
            var dur = 0;
            for(var page : progView.pages) if(page.isScheOn(milli+dur)) {
                avas.add(page);
                dur += page.tDur;
            }
            if(dur==0) for(var page : progView.pages) if(page.sches==null) {
                avas.add(page);
                dur += page.tDur;
            }
            if(dur!=0) {
                var start = milli / dur * dur;
                if(start < milli) {
                    do {
                        start += avas.get(curAva++).tDur;
                    } while(curAva < avas.size() && start<=milli);
                    start -= avas.get(--curAva).tDur;
                    syncMs = milli;
                    Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
                }
                avas.get(curAva).setMillis(start, milli);
                if(! progView.isShown()) setContentView(progView);
                state = 6;
                Util.println("Avas "+avas.size());
                return;
            }
        }
        waitTo = milli + 1000;
        if(state!=2) {
            setContentView(backView);
            state = 2;
        }
        Util.println("No Avas, back");
    }
}