//i450
//Zsombó István
//12. évfolyam
//Pécsi Leőwey Klára Gimnázium
//zsombo8774@gmail.com
//Android Studio 2.3.3
//Java Runtime Environment 1.8.0
//Kompatibilitás: Android 4.1 Jellybean - API 16 vagy bármely magasabb
package user.komal_i450;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import java.util.Random;
import android.os.Handler;

public class MainActivity extends AppCompatActivity {
//A program indulásakor meghívom a betoltes() metódust
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        betoltes();
    }
//Rajzoláshoz szükséges állandók
    final int meret = 100;
    final int DELAY = 30;
    Paint myPaint = new Paint();
//Az oszlopokat jelentő gombok ID gyűjteménye
    int[] azonositok = new int[]{R.id.n1,R.id.m1,R.id.n2,R.id.m2,R.id.e1,R.id.e2};
//Meghatározom, melyik gomb lenyomása melyik metódust hívja majd meg
//Meghívom a startup() és segitseg() metódusokat.
    void betoltes(){
        for (int j = 0; j < 6; j += 1){
            final int i = j;
            findViewById(azonositok[i]).setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    if (mozgathato && (motionEvent.getAction() == MotionEvent.ACTION_DOWN)) {
                        if (motionEvent.getY() > view.getHeight() / 2) {
                            felhuz(i);
                        } else {
                            lehuz(i);
                        }
                        ellenoriz();
                    }
                    return false;
                }
            });
        }
        findViewById(R.id.ujra).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == motionEvent.ACTION_DOWN){ujjatek();}
                return false;}
        });
        findViewById(R.id.sorok).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == motionEvent.ACTION_DOWN){sorvalto();}
                return false;}
        });
        findViewById(R.id.help).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == motionEvent.ACTION_DOWN){segitseg();}
                return false;}
        });
        findViewById(R.id.felad).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == motionEvent.ACTION_DOWN){feladas();}
                return false;}
        });
        findViewById(R.id.adatok).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == motionEvent.ACTION_DOWN){adatok();}
                return false;}
        });
        myPaint.setTextSize(meret * 9 / 10);
        myPaint.setTypeface(Typeface.create("Arial", Typeface.BOLD));
        startup();
        segitseg();
    }
//A tabla[][] 6 széles és olyan magas ahány sort kezelnem kell. Tárolja a megjelenítendő számjegyeket,
//valamint a 2. és 4. oszlopban a műveleti jelekhez rendelt számokat, melyek:
//1:+ 2:- 3:* 4:/ és -1:< 0:= +1:>
    int[][] tabla;
//A csuszas[6] tárolja melyik oszlopot hányszor csúsztattam le (+) avagy fel (-)
    int[] csuszas;
    int sorok = 4;
//mozgathato a feltétele annak, hogy a játékos csúsztathassa az oszlopokat.
    boolean mozgathato = false;
//Nullázom a tabla[][] és oszlop[] tartalmát, megkezdem egy új, helyes kezdőállapot generálását (general), majd összekeverem azt.
//Ha véletlen egy helyes megoldás kerülne a képernyőre, újra futtatom a metódust.
//R.id.ujra is meghívhatja.
    void startup() {
        tabla = new int[sorok][6];
        csuszas = new int[]{0,0,0,0,0,0};
        general();
        for (int oszlop = 0; oszlop < 6; oszlop += 1){
            ujrasorsol(oszlop);
            rajzol(oszlop, 0);
        }
        if (mindjo()){
            startup();}else{mozgathato = true;}
    }
//lehuz() és felhuz() metódusokat az oszlopokat jelentő gombok hívják meg.
//Az adott oszlop tartalmát a bemenetnek megfelelően fel vagy le mozgatom, a kieső rész megjelenik a megjelenő részen természetesen.
//Meghívom a rajzol() metódust a mozgatás irányának megfelelő haladas paraméterrel.
    void lehuz(int oszlop) {
        if (!animal[oszlop]) {
            int kezdo = tabla[0][oszlop];
            for (int i = 0; i < sorok - 1; i += 1) {
                tabla[i][oszlop] = tabla[i + 1][oszlop];
            }
            tabla[sorok - 1][oszlop] = kezdo;
            rajzol(oszlop, +1);
            csuszas[oszlop] += 1;
        }
    }
    void felhuz(int oszlop) {
        if (!animal[oszlop]) {
            int kezdo = tabla[sorok - 1][oszlop];
            for (int i = sorok - 2; i >= 0; i -= 1) {
                tabla[i + 1][oszlop] = tabla[i][oszlop];
            }
            tabla[0][oszlop] = kezdo;
            rajzol(oszlop, -1);
            csuszas[oszlop] -= 1;
        }
    }
    Handler myHandler = new Handler();
//Előjelfüggvény.
    public int sgn(double a){if(a>0){return 1;}else{if(a<0){return -1;}else{return 0;}}}
//Kiszámolja az első két számjegyen a köztük lévő műveleti jellel végzett művelet eredményét.
    public double muvelet(int n1, int m1, int n2){
        switch (m1) {
            case 1:
                return n1 + n2;
            case 2:
                return n1 - n2;
            case 3:
                return n1 * n2;
            default:
                return (double) n1 / (double) n2;
        }
    }
//Meghatározza, hogy a tabla[][] egyik sorában helyes-e a reláció.
    public boolean helyes(int n1, int m1, int n2, int m2, int e1, int e2) {
        return (sgn(muvelet(n1,m1,n2) - (10*e1+e2)) == m2);
    }
//Meghatározza azt a legkisebb nemnegatív egészt, mellyel A kongruens modulo B.
    public int mod(int a, int b){ //a mod b
        int m = a % b;
        if (m >= 0) { return m; } else { return m + b; }
    }
//Keresi a táblázatban a hibás sort. Ha nincs, akkor minden sor helyes, tehát a játékos megoldotta a feladványt.
    public boolean mindjo(){
        boolean megjo = true;
        for (int i = 0; (i < sorok ) && megjo; i += 1){
            megjo = helyes(tabla[i][0],tabla[i][1],tabla[i][2],tabla[i][3],tabla[i][4],tabla[i][5]);
        }
        return megjo;
    }
//A játékos minden lépése meghívja az ellenoriz() metódust. Vizsgálja, hogy megoldotta-e a játékos a feladványt.
    void ellenoriz(){
        if (mindjo()){
            mozgathato = false;
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Gratulálok!");
            builder.setMessage("Feladvány megoldva, minden sorban helyes a reláció!");
            builder.setPositiveButton("HURRÁ!",null);
            builder.show();
        }
    }
//Egy véletlen egész számot ad [min,max] zárt-zárt intervallumból.
    public int rsz(int min, int max){
        Random r = new Random();
        return min + r.nextInt(max-min+1);
    }
//Véletlenszerűen valamennyivel elcsúsztat egy bizonyos oszlopot. A helyes kezdőállapot legenerálása után használom annak megkeveréséhez.
    void ujrasorsol(int oszlop){
        int[] masolat = new int[sorok];
        for (int i = 0; i < sorok; i += 1){masolat[i] = tabla[i][oszlop];}
        int r = rsz(0,127);
        for (int i = 0; i < sorok; i += 1){tabla[i][oszlop] = masolat[mod((i + r), sorok)];}
        csuszas[oszlop] += mod(r, sorok);
    }
//Véletlenszerűen meghatározom az első két számjegyet és a köztük lévő műveleti jelet.
//Amennyiben az eredmény (utolsó két oszlop) így lehet pozitív egész, 1/2 eséllyel egyenlőséget készítek, különben:
//1/5 eséllyel egy valószínűleg nagyobb, különben egy valószínűbben kisebb eredmény készítek, és meghatározom a megfelelő relációt.
    void general(){
        for (int i = 0; i < sorok; i += 1){
            int n1 = rsz(1,9);
            int n2 = rsz(1,9);
            int m1 = rsz(1,4);
            double ered = muvelet(n1,m1,n2);
            if((ered == Math.floor(ered)) && ered > 0 && rsz(1,2)==1)
            {
                tabla[i][0] = n1;
                tabla[i][1] = m1;
                tabla[i][2] = n2;
                tabla[i][3] = 0;
                tabla[i][4] = (int)(Math.floor(ered / 10));
                tabla[i][5] = mod((int)Math.floor(ered), 10);
            }
            else
            {
                int e1;
                if (rsz(1,5)==1){e1 = rsz(0, 9);}else{e1 = rsz(0,(int)(Math.ceil(ered / 10)));}
                    int e2 = rsz(0, 9);
                    tabla[i][0] = n1;
                    tabla[i][1] = m1;
                    tabla[i][2] = n2;
                    tabla[i][3] = sgn(ered - (10 * e1 + e2));
                    tabla[i][4] = e1;
                    tabla[i][5] = e2;
            }
        }
    }
//Az R.id.sorok gomb hívja meg, ezzel változtatható a sorok száma.
    void sorvalto(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        LinearLayout linearLayout = new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        final SeekBar seekBar = new SeekBar(this);
        seekBar.setMax(7);
        seekBar.setProgress(sorok-3);
        final TextView textView = new TextView(this);
        TextViewCompat.setTextAppearance(textView,R.style.TextAppearance_AppCompat_Title);
        textView.setText("Sorok száma : " + sorok);
        SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
            @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                textView.setText("Sorok száma: " + String.valueOf(seekBar.getProgress() + 3));}
            @Override public void onStartTrackingTouch(SeekBar seekBar) {}
            @Override public void onStopTrackingTouch(SeekBar seekBar) {}};
        seekBar.setOnSeekBarChangeListener(onSeekBarChangeListener);
        builder.setPositiveButton("VÁLTOZTAT", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
             if ((seekBar.getProgress() + 3) != sorok) {sorok = seekBar.getProgress() + 3; startup();}
            }});

        builder.setTitle("");
        linearLayout.addView(textView);
        linearLayout.addView(seekBar);


        builder.setView(linearLayout);
        builder.show();

    }
//A játék indításakor jelenik meg, továbbá az R.id.help gomb hívhatja meg. Megjeleníti a játékszabályokat.
    void segitseg(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Segítség");
        builder.setMessage("Az oszlopok felső vagy alsó felébe kattintva azok minden eleme mozgatható fel vagy le eggyel. \n" +
                "A játék célja, hogy minden sorban helyes legyen a művelet. \n" +
                "Az oldalt lévő gombokkal kezdhető új játék, változtatható a sorok száma, vagy adható fel a játék. \n\n" +
                "Jó játékot!");
        builder.setPositiveButton("ÉRTEM", null);
        builder.show();
    }
//R.id.felad hívja meg, a játékosnak lehetősége nyílik feladni a játékot, ekkor minden oszlopra meghívom a huzamignemjo() metódust, ezzel látványosan megoldva a feladványt.
//(Így a játékos megbizonyosodhat róla, hogy a feladvány valóban megoldható volt az oszlopokat csúsztatva, hiszen láthatja a megoldás menetét.)
    void feladas(){
        if(mozgathato){
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Biztosan feladod?");
            builder.setMessage("Amennyiben igen, a feladvány megoldása lesz látható.");
            builder.setNegativeButton("MÉGSEM", null);
            builder.setPositiveButton("Igen, feladom.", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    mozgathato = false;
                    for (int oszlop = 0; oszlop < 6; oszlop += 1) {
                        huzamignemjo(oszlop);
                        }
                    }
            });
            builder.show();
        }
    }
//R.id.ujra hívja meg, elfogadáskor startup() új játék kezdődik.
    void ujjatek(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Szeretnél egy új játékot kezdeni?");
        builder.setPositiveButton("IGEN", new DialogInterface.OnClickListener() {
            @Override public void onClick(DialogInterface dialogInterface, int i) {
                startup();}});
        builder.setNegativeButton("MÉGSEM",null);
        builder.show();
    }
//Addig hívja meg egy bizonyos oszlop felhuz() metódusát, míg az oszlop elemei az eredetileg helyesnek generált helyre nem kerülnek.
    void huzamignemjo(final int oszlop){
        felhuz(oszlop);
        if (mod(csuszas[oszlop],sorok) != 0){
        myHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                huzamignemjo(oszlop);
            }
        },3*DELAY);
    }
    }
//animal[] tárolja, hogy éppen mozog-e egy bizonyos oszlop.
//Az animáció befejeztéig nem mozgatható ugyanazon oszlop a programbéli hibák kiküszöbölése érdekében.
    boolean[] animal = new boolean[]{false,false,false,false,false,false};
//Egy bizonyos oszlop megjelenítéséért felelős algoritmus.
//A haladas paraméter előjele megadja, hogy fel- vagy lefelé csúsztattam az oszlopot. Kezdeti értéke -1, 0 vagy +1.
//0 haladás esetén az algoritmus leáll, hisz nem kell mozgatni az oszlopokat, különben:
//DELAY-nyi idő elteltével újra meghívja magát, immár egy, a -10 vagy +10-hez 1-gyel közelebbi haladas paraméterrel.
//A haladas paraméter abszolútértéke megadja, hogy a csúsztatás előtti állapothoz képes hány tizedet haladt az animáció az azutáni állapot felé.
//Amint a haladas eléri a +-10-et, leáll az animáció, és az adott oszlop újra csúsztatható a játékos számára.
    void rajzol(final int oszlop, final int haladas) {
        Bitmap bmp = Bitmap.createBitmap(meret, sorok * meret, Bitmap.Config.ARGB_8888);
        Canvas g = new Canvas(bmp);
        animal[oszlop] = true;
        for (int i = -1; i < sorok + 1; i +=1){
            String ki;
            int q = mod(i - sgn(haladas), sorok);
            switch(oszlop){
                case 1 : ki = new String[]{"+","-","*","÷"}[tabla[q][oszlop]-1]; break;
                case 3 : ki = new String[]{"<","=",">"}[tabla[q][oszlop]+1]; break;
                default : ki = String.valueOf(tabla[q][oszlop]);}
            g.drawText(ki,20,
                    meret * ( i + (float)0.99 - (float)haladas / (float)10 )
                    , myPaint);
        }
        if (mod(haladas,10) != 0){
            myHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    rajzol(oszlop,haladas+sgn(haladas));
                }
            },DELAY);}
        else {animal[oszlop] = false;}
        //else{mozgathato=true;}

        ImageButton btn=(ImageButton)findViewById(azonositok[oszlop]);
        btn.setImageBitmap(bmp);
    }
//R.id.adatok hívja meg. A feladat adatait és engem tartalmaz.
    void adatok(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Adatok");
        builder.setMessage("i450 \n" +
                "Zsombó István \n" +
                "12. évfolyam \n" +
                "Pécsi Leőwey Klára Gimnázium \n" +
                "zsombo8774@gmail.com \n" +
                "Android Studio 2.3.3 \n" +
                "JRE 1.8.0 \n" +
                "Android 4.1 Jellybean - API 16");
        builder.show();
    }
}