/* - od vrstice 40 naprej je nova koda za millis
*/

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 2;    // the number of the pushbutton pin
const int ledPin =  13;     // the number of the LED pin
const int n = 100;          // čas intervala v ms
const int timer = 250;      // število intervalov (* čas intervala = čas aktivnosti)

const int rolete = 8;       // število rolet
const int stolpci = 5;      // število stanj na roleto (stara tipka, tipka, časovnik, smer, vklop)
const int releji = 2;       // število relejev na roleto
const int arrayRolete [rolete][releji] {{30, 31}, {32, 33}, {34, 35}, {36, 37}, {38, 39}, {40, 41}, {42, 43}, {44, 45}}; // vsaka roleta ima 2 kontrolna pina za 2 releja, 1-smer, 2-vklop, za vsak rele nastavimo številko pina, ki se uporablja

// variables will change:
int gumb = 0;               // variable for reading the pushbutton status
int gumbOld = 0;            // variable for reading the pushbutton status

int cas = 0;                // glavni timer
int smer = 0;               // 3ST; 1 - gor, 0 - dol
int motor = 0;              // SSR; 1 - premik, 0 - miruje

// nastavimo spremenjlivke za sprehod čez arraye
int t = 0;                  // števec za časovnik
int i = 0;                  // zaporedna številka rolete
int j = 0;                  // zaporedna številka stolpca
const int a = 0;            // stara tipka - archive
const int b = 1;            // tipka - button
const int c = 2;            // časovnik - cas
const int d = 3;            // smer - direction
const int e = 4;            // vklop - engine

// tabela za rolete z stanji časovnikom in tipkami
int arrayRun [rolete][stolpci] = {0}; // vsaka roleta ima 5 stolpcev, tu vse sočasno nastavimo na 0 (kar ni definirano je 0)

// nastavimo funkcijo za premik
void moveRoletePrint (); // prototip funkcije
void moveRolete (); // prototip funkcije

/*****************************************************************************************************/
// nova koda za millis - do tu še ni bilo nič spremenjeno, stara koda je naprej od void setup{}
/* 
unsigned long curr = 0;
unsigned long prev = 0;
int interval = 100;
curr = millis();
if( (curr - prev) > interval ){
        prev = curr;
        Serial.println("Beep!");
    }
*/

// luči imamo 19 (8 spodaj, 11 zgoraj)
// rolet imamo 8 (2 spodaj, 6 zgoraj)
// gumbov imamo 49 (24 zgoraj, 13 spodaj in 12 na kontrolni plošči)
// Z gumbov in 2 funkciji (roleta long press, luč short press)

// TODO: sistem zapisa shift,mux in direktne vezave v gumbe

const int numBits = 8;   // koliko bitni so shift registri
const int numChain = 2;  // koliko shift registrov je vezanih skupaj

// nastavimo število shift registrov in koliko bitni so (2*8=16)
const int shiftBit = numBits*numChain;
const int shiftNo = 4;

const int pulseWidth = 10;      // pulse width in microseconds
const int clockPin = 8;  // CP
const int latchPin = 9;  // PL
const int ArrayDataPin[shiftNo] = {10,11,12,13}; // Q7


// 3d array za staro/novo stanje shift registrov
const bool ArrayShiftState[shiftBit][shiftNo][2] = {0};

const int ukazi = 31;

// povezovalna tabela med stikalom in funkcijami:
// 0 ni ukaza
//* 1xx je pritličje
//  - 100 master spodaj
//  - 101 - dnevna 1 - TV
//  - 102 - dnevna 2 - sedežna
//  - 103 - jedilnica 1 - miza
//  - 104 - jedilnica 2 - hladilnik
//  - 105 - hodnik - spodaj
//  - 106 - predsoba
//  - 107 - kuhinja
//  - 108 - WC
//** 2xx je nadstropje
//  - 200 master zgoraj
//  - 201 - spalnica
//  - 202 - stopnišče (hodnik) - zgoraj
//  - 203 - Gašper 1 - miza
//  - 204 - Gašper 2 - postelja
//  - 205 - Matevž 1 - miza
//  - 206 - Matevž 2 - postelja
//  - 207 - telovadnica 1 - J
//  - 208 - telovadnica 2 - S
//  - 209 - pralnica
//  - 210 - kopalnica
//  - 211 - podstrešje
//*** 3xx so rolete
//  - 300 master rolete
//  - 301 - roleta spalnica
//  - 302 - roleta Gašper
//  - 303 - roleta Matevž
//  - 304 - roleta telovadnica J
//  - 305 - roleta telovadnica S
//  - 306 - roleta pralnica
//  - 307 - roleta dnevna
//  - 308 - roleta kuhinja
//**** 400 master vse luči

const int gumbi = 49

// TODO: prestaviti to tabeli v 2D, da lahko enako kot se berejo stikala iz SHIFT registrov
//       tudi prirerajo vrednosti, prav tako za milis old in new mora biti 2D matrika
//      - I in J se ne pišeta posebej v tabelo samo kot komentar
//      - uskladiti "šifrant" z stanji in izhodi
//      - naslednji korak je povezava akcije z izhodom
//      - dodati časovnike na izhod

// vsak gumb ima določen vhod (shift register, PIN) in 2 funkciji 1-short, 2-long
// ista funkcija pomeni, da kratek in dolg press izvede enak ukaz
const int arrayGumbi [ukazi][4] {
/************************/
/* stikalna plošča - 12 */
/************************/
  {0,  0, 0, 300},    //  0 - master rolete
  {0,  1, 101, 101},  //  1 - dnevna 1
  {0,  2, 102, 102},  //  2 - dnevna 2
  {0,  3, 103, 103},  //  3 - jedilnica 1
  {0,  4, 104, 104},  //  4 - jedilnica 2
  {0,  5,  105, 105},  //  5 - hodnik
  {0,  6, 106, 106},  //  6 - predsoba
  {0,  7, 107, 107},  //  7 - kuhinja
  {0,  8, 108, 108},  //  8 - WC
  {0,  9, 0, 307},    //  9 - roleta dnevna
  {0, 10, 0, 308},   // 10 - roleta kuhinja
  {0, 11, 0, 100},   // 11 - master spodaj
/****************/
/* gumbi spodaj */
/****************/
// dnevna/jedilnica - 1x4
  {1,  0, 101, 307}, // 12 - levo zgoraj - sedežna
  {1,  1, 102, 307}, // 13 - levo spodaj - TV
  {1,  2, 103, 307}, // 14 - desno zgoraj - jedilnica miza
  {1,  3, 104, 307}, // 15 - desno spodaj - jdilnica hladilnik
// kuhinja/jedilnica - 1x3
  {1,  4, 103, 308}, // 16 - levo zgoraj - jedilnica miza
  {1,  5, 104, 308}, // 17 - levo spodaj - jedilnica hladilnik
  {1,  6, 107, 308}, // 18 - desno - kuhinja
// hodnik/stopnišče - 2x1
  {1,  7, 105, 100}, // 19 - desni - hodnik spodaj
  {1,  8, 202, 200}, // 20 - levi - hodnik zgoraj
// hodnik - 2x1
  {1,  9, 105, 105}, // 21 - desni - hodnik spodaj
  {1, 10, 106, 106}, // 22 - levi - predsoba
// predsoba - 1x1 velik
  {1, 11, 106, 400}, // 23 - vhod - predsoba
// WC - 1x1 velik
  {1, 12, 108, 108}, // 24 - hodnik - WC
/****************/
/* gumbi zgoraj */
/****************/
// spalnica - 2x1 mali + 1x1 velik
  {2,  0, 201, 301}, // 25 - Irena
  {2,  1, 201, 301}, // 26 - Mitja
  {2,  2, 201, 301}, // 27 - vhod
// Gašper - 3x2  
  {3,  0, 203, 302}, // 28 - vhod L - okno
  {3,  1, 204, 302}, // 29 - vhod D - postelja
  {3,  2, 203, 302}, // 30 - postelja D - okno
  {3,  3, 204, 302}, // 31 - postelja L - postelja
  {3,  4, 203, 302}, // 32 - miza L - postelja
  {3,  5, 204, 302}, // 33 - miza D - okno
// Matevž - 3x2  
  {3,  6, 205, 303}, // 34 - vhod L - postelja
  {3,  7, 206, 303}, // 35 - vhod D - okno
  {3,  8, 205, 303}, // 36 - postelja D - okno
  {3,  9, 206, 303}, // 37 - postelja L - postelja
  {3, 10, 205, 303}, // 38 - miza L - postelja
  {3, 11, 206, 303}, // 39 - miza D - okno
// telovadnica - 2x2 mali
  {2,  3, 207, 304}, // 40 - telovadnicaJ L
  {2,  4, 208, 305}, // 41 - telovadnicaJ D
  {2,  5, 207, 304}, // 42 - telovadnicaS L
  {2,  6, 208, 305}, // 43 - telovadnicaS D
// pralnica - 2x1 velik
  {2,  7, 209, 306}, // 44 - telovadnica
  {2,  8, 209, 306}, // 45 - hodnik
// kopalnica - 1x1 velik
  {2,  9, 210, 210}, // 46 - kopalnica
// stopnišče - 2x1
  {2, 10, 105, 100}, // 47 - stopnišče L
  {2, 11, 202, 200}  // 48 - stopnišče D
  }; // vsak gumb ima določen vhod (pin/shift register/multiplexor), vrstni red in 2 funkciji 1-short, 2-long


unsigned long TrenutniCas; // shranimo millis na začetku zanke

// tabela za gumbe z stanji in tipkami 
// (stara tipka, tipka, časovnik, smer, vklop)
int arrayButton [gumbi][5] = {0}; // vsako stikalo ima 5 stolpcev, tu vse sočasno nastavimo na 0 (kar ni definirano je 0)
unsigned long arrayButtonTimer [gumbi] = {0}; // časovnik damo posebej, ker ostlaih 4 spremenljivk ne potrebujemo v takšni dolžini
// v funkcijah prestavimo c na arrayButtonTimer in ne na arrayButton [i][c]

 // prototipi funkcij
void readButton();
void setRelay();

void ReadAll165(); // prototip funkcije za branje vseh shift registrov

/****************************************************************************************************/

// funkcija za nastavitev ob zagonu
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);

  pinMode( clockPin, OUTPUT);   // clock signal, idle LOW
  digitalWrite( clockPin, LOW);
  pinMode( latchPin, OUTPUT);   // latch (copy input into registers), idle HIGH
  digitalWrite( latchPin, HIGH);

  // nastavimo vse tipke na vhod
  //TODO - array za vhode tipk oz. multiplexer

  // nastavimo vse releje na izhod
  for (i = 0; i < rolete; i++) {
    for (j = 0; j < releji; j++) {
      pinMode(arrayRolete[i][j], OUTPUT);

      /*if (j == 1) {
        digitalWrite(arrayRolete[i][j], HIGH);
        delay(400);
        digitalWrite(arrayRolete[i][j], LOW);
        delay(200);
      } else {
        digitalWrite(arrayRolete[i][j], LOW);
        delay(400);
        digitalWrite(arrayRolete[i][j], HIGH);
        delay(200);
      }*/
    }
  }


  /*
    motor = 1; // vklopimo master motor
    smer = 1; // nastavimo master smer gor

    // vklopimo motorje in odpremo rolete ob zagonu
    for (i = 0; i < rolete; i++) {
      arrayRun[i][d] = 1; // nastvimo smer na gor
      arrayRun[i][e] = 1; // vklopimo motor
    }

    // gremo čez časovni interval
    for (t = timer; t > 0; t--) {
      // poženemo motorje
      moveRolete();

      delay(n); // počakaj interval
    }

    Serial.println("konec"); // izpis

    motor = 0; // izklopimo master motor
    smer = 0; // nastavimo master smer dol

    for (i = 0; i < rolete; i++) {
      arrayRun[i][d] = 0; // nastvimo smer na dol
      arrayRun[i][e] = 0; // izklopimo motor
    }
  */
}

// glavna zanka
void loop() {

  // preberemo vse shift registre
  ReadAll165();

  // preberemo stanje master gumba
  gumb = digitalRead(buttonPin);

  // normaliziramo stanje gumba
  if (gumb == HIGH) {
    gumb = 1;
  } else {
    gumb = 0;
  }

  // TODO: preberemo stanje gumbov oz. multiplexorja

  // če se je spremenilo stanje in je gumb pritisnjen potem izvedemo spremembo sicer teče timer
  if (gumbOld != gumb && gumb == 1) {

    ////////////////////////////

    if (motor == 0) {
      // turn LED on:
      digitalWrite(ledPin, HIGH);
      Serial.println("premakni");

      // vklopimo motorje
      motor = 1;
      for (i = 0; i < rolete; i++) {
        arrayRun[i][e] = 1;
      }

      // nastavimo časovnike
      cas = timer;
      for (i = 0; i < rolete; i++) {
        arrayRun[i][c] = timer;
      }
    } else {
      // turn LED off:
      digitalWrite(ledPin, LOW); // izklopimo luč
      Serial.println("ustavi"); // izpis

      // izklopimo motorje
      motor = 0;
      for (i = 0; i < rolete; i++) {
        arrayRun[i][e] = 0;
      }

      // obrnemo smeri
      if (smer == 0) {
        smer = 1;
        for (i = 0; i < rolete; i++) {
          arrayRun[i][d] = 1;
        }
      } else {
        smer = 0;
        for (i = 0; i < rolete; i++) {
          arrayRun[i][d] = 0;
        }
      }

      // pobrišemo časovnike
      cas = 0;
      for (i = 0; i < rolete; i++) {
        arrayRun[i][c] = 0;
      }
    }
  } else {
    // če je aktiven timer ga zmanjšamo za 1
    if (cas > 1) {
      cas--;
    } else if (cas == 1) { // ko pride na 1 ugasnemo motor, obrnemo smer in ponastavimo timer na 0
      // turn LED off:
      digitalWrite(ledPin, LOW); // izklopimo luč
      Serial.println("konec"); // izpis

      // izklopimo motorje
      motor = 0; // izklopimo motor
      for (i = 0; i < rolete; i++) {
        arrayRun[i][e] = 0;
      }

      // obrnemo smeri
      if (smer == 0) {
        smer = 1;
        for (i = 0; i < rolete; i++) {
          arrayRun[i][d] = 1;
        }
      } else {
        smer = 0;
        for (i = 0; i < rolete; i++) {
          arrayRun[i][d] = 0;
        }
      }

      cas = 0; // pobrišemo časovnike
      for (i = 0; i < rolete; i++) {
        arrayRun[i][c] = 0;
      }
    }
    ///////////////////////////
  }

  gumbOld = gumb; // zapišemo stanje ob branju za naslednji krog

  // TODO: ponovimo za vse gumbe, kot za master

  //izvedemo ukaze glede na vsebino tabele
  moveRolete();

  delay(n); // počakaj interval

}


// poženemo motorje z izpisi
void moveRoletePrint() {
  // pogledamo zaporedno roleto
  for (i = 0; i < rolete; i++) {
    Serial.print("roleta: ");
    Serial.print(i);

    // pogledamo v katero smer se vozi
    if (arrayRun[i][d] == 0) {
      Serial.print(" - dol");
      digitalWrite(arrayRolete[i][0], HIGH);
    } else {
      Serial.print(" - gor");
      digitalWrite(arrayRolete[i][0], LOW);
    }

    // pogledamo ali se vozi ali muruje
    if (arrayRun[i][e] == 0) {
      Serial.print(" - čakam ");
      digitalWrite(arrayRolete[i][1], LOW);
    } else {
      Serial.print(" - vozim ");
      digitalWrite(arrayRolete[i][1], HIGH);
    }
  }
  Serial.println(" ...");
}

// poženemo motorje brez izpisov
void moveRolete() {
  // pogledamo zaporedno roleto
  for (i = 0; i < rolete; i++) {
    //Serial.print("roleta: ");
    //Serial.print(i);

    // pogledamo v katero smer se vozi
    if (arrayRun[i][d] == 0) {
      //Serial.print(" - dol");
      digitalWrite(arrayRolete[i][0], LOW);
    } else {
      //Serial.print(" - gor");
      digitalWrite(arrayRolete[i][0], HIGH);
    }

    // pogledamo ali se vozi ali muruje
    if (arrayRun[i][e] == 0) {
      //Serial.print(" - čakam");
      digitalWrite(arrayRolete[i][1], LOW);
    } else {
      //Serial.print(" - vozim");
      digitalWrite(arrayRolete[i][1], HIGH);
    }
    //Serial.println(" ...");
  }
}

// preberemo vse shift registre naenkrat
void ReadAll165()
{
  // pošljemo impulz na vse 74HC165, da shranimo stanje
  digitalWrite( latchPin, LOW);    
  delayMicroseconds( pulseWidth);
  digitalWrite( latchPin, HIGH);

  // The first one that is read is the highest bit (input D7 of the 74HC165).
  for( int i=shiftBit-1; i>=0; i--) // 16
  {
    for( int j=shiftNo-1; j>=0; j--) // 4
    {
      if( digitalRead(ArrayDataPin[j]) == HIGH)
      {
        ArrayShiftState[i][j][1] = 1;
      } else {
        ArrayShiftState[i][j][1] = 0;
      }
    }

    // pošljemo impulz ure na vse shift registre, da dobimo naslednji podatek
    digitalWrite( clockPin, HIGH);
    delayMicroseconds( pulseWidth);
    digitalWrite( clockPin, LOW);
  }
}
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165