/*
 * Spreed Speak Freely.
 * Copyright (C) 2013-2014 struktur AG
 *
 * This file is part of Spreed Speak Freely.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
define(['underscore', 'text!partials/chat.html', 'text!partials/chatroom.html'], function(_, templateChat, templateChatroom) {

    return ["$compile", "safeDisplayName", "mediaStream", "safeApply", "desktopNotify", "translation", "playSound", "fileUpload", "randomGen", "buddyData", "$timeout", function($compile, safeDisplayName, mediaStream, safeApply, desktopNotify, translation, playSound, fileUpload, randomGen, buddyData, $timeout) {

        var displayName = safeDisplayName;
        var group_chat_id = "";

        var controller = ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {

            $scope.layout.chat = false;
            $scope.layout.chatMaximized = false;

            var ctrl = this;
            var rooms = ctrl.rooms = {};
            ctrl.visibleRooms = [];
            ctrl.group = group_chat_id;
            ctrl.get = function(id) {
                return ctrl.rooms[id];
            }

            $scope.currentRoom = null;
            $scope.currentRoomActive = false;
            $scope.getVisibleRooms = function() {
                var res = [];
                for (var i=0; i<ctrl.visibleRooms.length; i++) {
                    var r = rooms[ctrl.visibleRooms[i]];
                    if (!r || r.id === ctrl.group) {
                        continue;
                    }
                    res.push(r);
                }
                return res;
            };
            $scope.getGroupRoom = function() {
                return rooms[ctrl.group];
            };

            mediaStream.api.e.on("received.chat", function(event, id, from, data, p2p) {

                //console.log("received", data, id, from);

                var roomid = id;
                if (roomid === mediaStream.api.id) {
                    roomid = from;
                } else {
                    if (roomid !== ctrl.group && from !== mediaStream.api.id) {
                        console.log("Received chat message for invalid room", roomid, id, from);
                        return;
                    }
                }

                var with_message = !!data.Message;
                var room = rooms[roomid];
                if (!room) {
                    if (!with_message) {
                        return;
                    }
                    // No room with this id, get one with the from id
                    $scope.$emit("startchat", from, {restore: with_message});
                    room = rooms[from];
                }

                if (with_message && from !== mediaStream.api.id) {
                    room.newmessage = true;
                    room.peerIsTyping = "no";
                    room.p2p(!!p2p);
                }

                room.$broadcast("received", from, data);
                safeApply(room);

            });

            mediaStream.api.e.on("received.userleftorjoined", function(event, dataType, data) {
                var room = rooms[data.Id];
                if (room) {
                    switch (dataType) {
                    case "Left":
                        if (data.Status !== "soft") {
                            room.enabled = false;
                            room.$broadcast("received", data.Id, {Type: "LeftOrJoined", "LeftOrJoined": "left"});
                            safeApply(room);
                        }
                        break;
                    case "Joined":
                        if (!room.enabled) {
                            room.enabled = true;
                            _.delay(function() {
                                room.$broadcast("received", data.Id, {Type: "LeftOrJoined", "LeftOrJoined": "joined"});
                                safeApply(room);
                            }, 1000);
                        }
                        break;
                    default:
                        break;
                    }
                }
            });

            $scope.$parent.$on("startchat", function(event, id, options) {

                //console.log("startchat requested", event, id);
                if (id === group_chat_id) {
                    $scope.showGroupRoom(null, options);
                } else {
                    $scope.showRoom(id, {title: translation._("Chat with")}, options);
                }

            });

        }];

        var compile = function(tElement, tAttrs) {

            var chat = $compile(templateChatroom);
            return function(scope, iElement, iAttrs, controller) {

                var pane = iElement.find(".chatpane");

                scope.showGroupRoom = function(settings, options) {
                    var stngs = $.extend({title: translation._("Room chat")}, settings);
                    return scope.showRoom(controller.group, stngs, options);
                };

                scope.showRoom = function(id, settings, options) {
                    var options = $.extend({}, options);
                    var subscope = controller.rooms[id];
                    var index = controller.visibleRooms.length;
                    if (!subscope) {
                        console.log("Create new chatroom", [id]);
                        controller.visibleRooms.push(id);
                        subscope = controller.rooms[id] = scope.$new();
                        translation.inject(subscope);
                        subscope.id = id;
                        subscope.isgroupchat = id === controller.group ? true : false;
                        subscope.index = index;
                        subscope.settings = settings;
                        subscope.visible = false;
                        subscope.newmessage = false;
                        subscope.enabled = true;
                        subscope.peerIsTyping = "no";
                        subscope.firstmessage = true;
                        subscope.p2pstate = false;
                        subscope.active = false;
                        subscope.pending = 0;
                        if (!subscope.isgroupchat) {
                            buddyData.push(id);
                        }
                        subscope.hide = function() {
                            scope.hideRoom(id);
                        };
                        //TODO(longsleep): This is currently never called. Find a suitable way to clean up old chats.
                        subscope.kill = function() {
                            if (!subscope.isgroupchat) {
                                buddyData.pop(id);
                            }
                            scope.killRoom(id);
                        };
                        subscope.seen = function() {
                            subscope.pending = 0;
                            scope.$emit("chatseen", subscope.id);
                            if (subscope.newmessage) {
                                subscope.newmessage = false;
                                subscope.$broadcast("seen");
                            }
                        };
                        subscope.deactivate =function() {
                            scope.deactivateRoom();
                        };
                        subscope.toggleMax = function() {
                            scope.toggleMax();
                        };
                        subscope.sendChat = function(to, message, status, mid, noloop) {
                            //console.log("send chat", to, scope.peer);
                            var peercall = mediaStream.webrtc.findTargetCall(to);
                            if (message && !mid) {
                                mid = randomGen.random({hex: true});
                            };
                            if (peercall && peercall.peerconnection.datachannelReady) {
                                subscope.p2p(true);
                                // Send out stuff through data channel.
                                _.delay(function() {
                                    mediaStream.api.apply("sendChat", {
                                        send: function(type, data) {
                                            // We also send to self, to display our own stuff.
                                            if (!noloop) {
                                                mediaStream.api.received({Type: data.Type, Data: data, From: mediaStream.api.id, To: peercall.id});
                                            }
                                            return peercall.peerconnection.send(data);
                                        }
                                    })(to, message, status, mid);
                                }, 100);

                            } else {
                                subscope.p2p(false);
                                _.delay(function() {
                                    mediaStream.api.send2("sendChat", function(type, data) {
                                        if (!noloop) {
                                            //console.log("looped to self", type, data);
                                            mediaStream.api.received({Type: data.Type, Data: data, From: mediaStream.api.id, To: to});
                                        }
                                    })(to, message, status, mid);
                                }, 100);
                            }
                            return mid;
                        };
                        subscope.p2p = function(state) {
                            if (state !== subscope.p2pstate) {
                                subscope.p2pstate = state;
                                subscope.$broadcast("p2p", state);
                            }
                        };
                        //console.log("Creating new chat room", controller, subscope, index);
                        subscope.$on("submit", function(event, input) {
                            subscope.seen();
                            var mid = subscope.sendChat(event.targetScope.id, input);
                            event.targetScope.$broadcast("received", null, {Type: "Message", Status: {State: "sent", "Mid": mid}});
                        });
                        subscope.$on("submitseen", function(event, pending) {
                            //console.log("submitseen", pending);
                            subscope.sendChat(event.targetScope.id, null, {SeenMids: pending}, null, true);
                        });
                        subscope.$on("submitreceived", function(event, mid) {
                            subscope.sendChat(event.targetScope.id, null, {State: "delivered", Mid: mid}, null, true);
                        });
                        subscope.$on("typing", function(event, params) {
                            if (params.who === "local") {
                                var room = event.targetScope.id;
                                subscope.seen();
                                //console.log("typing event", params.status);
                                if (!subscope.isgroupchat) {
                                    // Transmit typing events to private chats.
                                    subscope.sendChat(event.targetScope.id, null, {Typing: params.status});
                                }
                            } else {
                                subscope.peerIsTyping = params.status;
                                //console.log("peer typing event", params.status, subscope.peerIsTyping);
                            }
                            safeApply(subscope);
                        });
                        subscope.$on("incoming", function(event, message, from, userid) {
                            if (from !== userid) {
                                subscope.pending++;
                                scope.$emit("chatincoming", subscope.id);
                            }
                            if (subscope.firstmessage || !desktopNotify.windowHasFocus) {
                                var room = event.targetScope.id;
                                // Make sure we are not in group chat or the message is from ourselves
                                // before we beep and shout.
                                if (!subscope.isgroupchat && from !== userid) {
                                    playSound.play("message1");
                                    desktopNotify.notify(translation._("Message from ")+displayName(from), message);
                                }
                                subscope.firstmessage = false;
                            }
                        });
                        chat(subscope, function(clonedElement, $scope) {

                            pane.append(clonedElement);
                            $scope.element=clonedElement;
                            $scope.visible = true;
                            if (options.autofocus) {
                                _.defer(function() {
                                    $scope.$broadcast("focus");
                                });
                            }

                            // Support drag and drop file uploads in Chat.
                            var namespace = "file_"+scope.id;
                            var binder = fileUpload.bindDrop(namespace, clonedElement, _.bind(function(files) {
                                console.log("File dragged", files);
                                _.each(files, _.bind(function(f) {
                                    var info = $.extend({id: f.id}, f.info);
                                    console.log("Advertising file", f, info);
                                    $scope.sendChat(subscope.id, "File", {FileInfo: info});
                                }, this));
                            }, this));
                            binder.namespace = function() {
                                // Inject own id into namespace.
                                return namespace+"_"+scope.myid;
                            };

                        });
                    } else {

                        if (options.restore) {
                            if (!subscope.visible) {
                                controller.visibleRooms.push(id);
                                subscope.index = index;
                                subscope.visible = true;
                            }
                        }
                        if (options.autofocus && subscope.visible) {
                            subscope.$broadcast("focus");
                        }

                    }

                    if (!options.noactivate) {
                        scope.activateRoom(subscope.id, true);
                    }

                    if (options.restore && !options.noenable) {
                        if (!scope.layout.chat) {
                            scope.layout.chat = true;
                        }
                    }

                    safeApply(subscope);
                    return subscope;
                };

                scope.hideRoom = function(id) {
                    var subscope = controller.rooms[id];
                    if (!subscope) {
                        console.log("hideRoom called for unknown room", id);
                        return;
                    }
                    var element = subscope.element;
                    var index = subscope.index;
                    controller.visibleRooms.splice(index, 1);
                    subscope.visible=false;
                    subscope.firstmessage=true;
                    // Refresh index of the rest of the rooms.
                    _.each(controller.visibleRooms, function(id, idx) {
                        var s = controller.rooms[id];
                        //console.log("updated idx", idx, s.index);
                        s.index = idx;
                    });
                    if (scope.currentRoom === subscope) {
                        scope.currentRoom = null;
                        scope.currentRoomActive = false;
                    }
                    if (!controller.visibleRooms.length) {
                        scope.showGroupRoom(null, {restore: true, noenable: true, noactivate: true});
                        // If last visible room was removed, hide chat.
                        scope.layout.chat = false;
                    }
                };

                scope.killRoom = function(id) {
                    scope.hideRoom(id);
                    var subscope = controller.rooms[id];
                    if (!subscope) {
                        return;
                    }
                    delete controller.rooms[id];
                    $timeout(function() {
                        subscope.$destroy();
                    }, 0);
                };

                scope.toggleMax = function() {
                    scope.layout.chatMaximized = !scope.layout.chatMaximized;
                };

                scope.activateRoom = function(id, active) {
                    var subscope = controller.rooms[id];
                    if (!subscope) {
                        return;
                    }
                    var visible = !!scope.layout.chat;
                    var flip = false;
                    //console.log("toggleActive", active, id, scope.currentRoom, scope.currentRoom == subscope, subscope.active);
                    if (scope.currentRoom == subscope) {
                        subscope.active = active;
                        scope.currentRoomActive = true;
                        if (visible) {
                            flip = true;
                        }
                    } else {
                        if (scope.currentRoom) {
                            scope.currentRoom.active = false;
                            //scope.currentRoom.hide();
                            if (visible) {
                                flip = true;
                            }
                        }
                        if (active) {
                            scope.currentRoom = subscope;
                            scope.currentRoomActive = true;
                        }
                        subscope.active = active;
                    }
                    if (flip) {
                        pane.toggleClass("flip");
                    }
                };

                scope.deactivateRoom = function() {
                    scope.currentRoomActive = false;
                };

                scope.$watch("layout.chat", function(chat) {
                    if (!chat) {
                        pane.removeClass("flip");
                    }
                    scope.layout.chatMaximized = false;
                });

                scope.$on("room", function(event, room) {
                    var subscope = scope.showGroupRoom(null, {restore: true, noenable: true, noactivate: true});
                    if (room) {
                        var msg = translation._("You are now in room %s ...", room);
                        subscope.$broadcast("display", null, $("<i><span>"+msg+"</span></i>"));
                    }
                });

            };

        };

        return {
            restrict: 'E',
            replace: true,
            scope: true,
            template: templateChat,
            controller: controller,
            compile: compile
        }

    }];

});
