/*
 * spa.model.js
 * Moduł Modelu.
*/

/*jslint         browser : true, continue : true,
  devel  : true, indent  : 2,    maxerr   : 50,
  newcap : true, nomen   : true, plusplus : true,
  regexp : true, sloppy  : true, vars     : false,
  white  : true
*/
/*global TAFFY, $, spa */

spa.model = (function () {
  'use strict';
  var
    configMap = { anon_id : 'a0' },
    stateMap  = {
      anon_user      : null,
      cid_serial     : 0,
      people_cid_map : {},
      people_db      : TAFFY(),
      user           : null
    },

    isFakeData = true,

    personProto, makeCid, clearPeopleDb, completeLogin,
    makePerson, removePerson, people, initModule;

// Interfejs API obiektu people.
// ---------------------
// Obiekt people jest dostępny w spa.model.people.
// Obiekt people zapewnia metody i zdarzenia służące do zarządzania kolekcją obiektów person. Jego metody publiczne obejmują:
//   * get_user () — zwraca obiekt person bieżącego użytkownika. Jeśli bieżący użytkownik nie jest zalogowany, zwracany jest anonimowy obiekt person.
//   * get_db () — zwraca wstępnie posortowaną bazę danych TaffyDB wszystkich obiektów person, w tym bieżącego użytkownika.
//   * get_by_cid( <id_klienta> ) — zwraca obiekt person z dostarczonym unikatowym identyfikatorem.
//   * login( <nazwa_użytkownika> ) — logowanie jako użytkownik z dostarczoną nazwą użytkownika. Obiekt bieżącego użytkownika zostanie zmieniony w celu odzwierciedlenia nowej tożsamości.
//   * logout() — przywraca obiekt bieżącego użytkownika do obiektu użytkownika anonimowego.
//
// Globalne zdarzenia niestandardowe jQuery publikowane przez ten obiekt obejmują:
//   * 'spa-login' — publikowane, gdy zakończony zostaje proces logowania użytkownika. Zaktualizowany obiekt użytkownika jest dostarczany jako dane.
//   * 'spa-logout' — publikowane po zakończeniu procesu wylogowania. Obiekt poprzedniego użytkownika jest dostarczany jako dane.
//
// Każda osoba jest reprezentowana przez obiekt person. Obiekty person dostarczają następujące metody:
//   * get_is_user() — zwraca true, jeśli obiektem jest bieżący użytkownik.
//   * get_is_anon() — zwraca true, jeśli obiektem jest anonimowy użytkownik.
//
// Do atrybutów obiektu person należą:
//   * cid — łańcuch znaków dla identyfikatora klienta. Jest zawsze zdefiniowany i różni się od atrybutu id tylko wtedy, kiedy dane klienta nie są zsynchronizowane z back-endem.
//   * id — unikatowy identyfikator. Może być niezdefiniowany, jeśli obiekt nie jest zsynchronizowany z back-endem.
//   * name — łańcuch znaków dla nazwy użytkownika.
//   * css_map — mapa atrybutów wykorzystywana do prezentacji awatara.
//

  personProto = {
    get_is_user : function () {
      return this.cid === stateMap.user.cid;
    },
    get_is_anon : function () {
      return this.cid === stateMap.anon_user.cid;
    }
  };

  makeCid = function () {
    return 'c' + String( stateMap.cid_serial++ );
  };

  clearPeopleDb = function () {
    var user = stateMap.user;
    stateMap.people_db      = TAFFY();
    stateMap.people_cid_map = {};
    if ( user ) {
      stateMap.people_db.insert( user );
      stateMap.people_cid_map[ user.cid ] = user;
    }
  };

  completeLogin = function ( user_list ) {
    var user_map = user_list[ 0 ];
    delete stateMap.people_cid_map[ user_map.cid ];
    stateMap.user.cid     = user_map._id;
    stateMap.user.id      = user_map._id;
    stateMap.user.css_map = user_map.css_map;
    stateMap.people_cid_map[ user_map._id ] = stateMap.user;

    // Kiedy dodajemy czat, powinniśmy dołączyć go tutaj.
    $.gevent.publish( 'spa-login', [ stateMap.user ] );
  };

  makePerson = function ( person_map ) {
    var person,
      cid     = person_map.cid,
      css_map = person_map.css_map,
      id      = person_map.id,
      name    = person_map.name;

    if ( cid === undefined || ! name ) {
      throw 'id i nazwa klienta są wymagane';
    }

    person         = Object.create( personProto );
    person.cid     = cid;
    person.name    = name;
    person.css_map = css_map;

    if ( id ) { person.id = id; }

    stateMap.people_cid_map[ cid ] = person;

    stateMap.people_db.insert( person );
    return person;
  };

  removePerson = function ( person ) {
    if ( ! person ) { return false; }
    // Nie można usunąć osoby anonimowej.
    if ( person.id === configMap.anon_id ) {
      return false;
    }

    stateMap.people_db({ cid : person.cid }).remove();
    if ( person.cid ) {
      delete stateMap.people_cid_map[ person.cid ];
    }
    return true;
  };

  people = (function () {
    var get_by_cid, get_db, get_user, login, logout;

    get_by_cid = function ( cid ) {
      return stateMap.people_cid_map[ cid ];
    };

    get_db = function () { return stateMap.people_db; };

    get_user = function () { return stateMap.user; };

    login = function ( name ) {
      var sio = isFakeData ? spa.fake.mockSio : spa.data.getSio();

      stateMap.user = makePerson({
        cid     : makeCid(),
        css_map : {top : 25, left : 25, 'background-color':'#8f8'},
        name    : name
      });

      sio.on( 'userupdate', completeLogin );

      sio.emit( 'adduser', {
        cid     : stateMap.user.cid,
        css_map : stateMap.user.css_map,
        name    : stateMap.user.name
      });
    };

    logout = function () {
      var is_removed, user = stateMap.user;
      // when we add chat, we should leave the chatroom here

      is_removed    = removePerson( user );
      stateMap.user = stateMap.anon_user;

      $.gevent.publish( 'spa-logout', [ user ] );
      return is_removed;
    };

    return {
      get_by_cid : get_by_cid,
      get_db     : get_db,
      get_user   : get_user,
      login      : login,
      logout     : logout
    };
  }());

  initModule = function () {
    var i, people_list, person_map;

    // Inicjowanie osoby anonimowej.
    stateMap.anon_user = makePerson({
      cid   : configMap.anon_id,
      id    : configMap.anon_id,
      name : 'anonimowy'
    });
    stateMap.user = stateMap.anon_user;

    if ( isFakeData ) {
      people_list = spa.fake.getPeopleList();
      for ( i = 0; i < people_list.length; i++ ) {
        person_map = people_list[ i ];
        makePerson({
          cid     : person_map._id,
          css_map : person_map.css_map,
          id      : person_map._id,
          name    : person_map.name
        });
      }
    }
  };

  return {
    initModule : initModule,
    people     : people
  };
}());
