// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package codeu.chat.server;

import java.util.Comparator;
import java.util.Set;
import java.util.HashSet;

import codeu.chat.common.ConversationHeader;
import codeu.chat.common.ConversationPayload;
import codeu.chat.common.Interests;
import codeu.chat.common.LinearUuidGenerator;
import codeu.chat.common.Message;
import codeu.chat.common.User;
import codeu.chat.common.UserAccessLevel;
import codeu.chat.util.Time;
import codeu.chat.util.Uuid;
import codeu.chat.util.store.Store;
import codeu.chat.util.store.StoreAccessor;

public final class Model {

  private static final Comparator<Uuid> UUID_COMPARE = new Comparator<Uuid>() {

    @Override
    public int compare(Uuid a, Uuid b) {

      if (a == b) { return 0; }

      if (a == null && b != null) { return -1; }

      if (a != null && b == null) { return 1; }

      final int order = Integer.compare(a.id(), b.id());
      return order == 0 ? compare(a.root(), b.root()) : order;
    }
  };

  private static final Comparator<Time> TIME_COMPARE = new Comparator<Time>() {
    @Override
    public int compare(Time a, Time b) {
      return a.compareTo(b);
    }
  };

  private static final Comparator<String> STRING_COMPARE = String.CASE_INSENSITIVE_ORDER;

  private final Store<Uuid, User> userById = new Store<>(UUID_COMPARE);
  private final Store<Time, User> userByTime = new Store<>(TIME_COMPARE);
  private final Store<String, User> userByText = new Store<>(STRING_COMPARE);

  private final Store<Uuid, ConversationHeader> conversationById = new Store<>(UUID_COMPARE);
  private final Store<Time, ConversationHeader> conversationByTime = new Store<>(TIME_COMPARE);
  private final Store<String, ConversationHeader> conversationByText = new Store<>(STRING_COMPARE);

  private final Store<Uuid, ConversationPayload> conversationPayloadById = new Store<>(UUID_COMPARE);

  private final Store<Uuid, Message> messageById = new Store<>(UUID_COMPARE);
  private final Store<Time, Message> messageByTime = new Store<>(TIME_COMPARE);
  private final Store<String, Message> messageByText = new Store<>(STRING_COMPARE);

  private final Store<Uuid, Interests> interestsByUserId = new Store<>(UUID_COMPARE);
  private final Store<Uuid, Set<UserAccessLevel>> accessLevelsByConvId = new Store<>(UUID_COMPARE);

  public void add(User user) {
    userById.insert(user.id, user);
    userByTime.insert(user.creation, user);
    userByText.insert(user.name, user);
  }

  public StoreAccessor<Uuid, User> userById() {
    return userById;
  }

  public StoreAccessor<Time, User> userByTime() {
    return userByTime;
  }

  public StoreAccessor<String, User> userByText() {
    return userByText;
  }

  public void add(ConversationHeader conversation) {
    conversationById.insert(conversation.id, conversation);
    conversationByTime.insert(conversation.creation, conversation);
    conversationByText.insert(conversation.title, conversation);
    conversationPayloadById.insert(conversation.id, new ConversationPayload(conversation.id));
  }

  public StoreAccessor<Uuid, ConversationHeader> conversationById() {
    return conversationById;
  }

  public StoreAccessor<Time, ConversationHeader> conversationByTime() {
    return conversationByTime;
  }

  public StoreAccessor<String, ConversationHeader> conversationByText() {
    return conversationByText;
  }

  public StoreAccessor<Uuid, ConversationPayload> conversationPayloadById() {
    return conversationPayloadById;
  }

  public void add(Message message) {
    messageById.insert(message.id, message);
    messageByTime.insert(message.creation, message);
    messageByText.insert(message.content, message);
  }

  public StoreAccessor<Uuid, Message> messageById() {
    return messageById;
  }

  public StoreAccessor<Time, Message> messageByTime() {
    return messageByTime;
  }

  public StoreAccessor<String, Message> messageByText() {
    return messageByText;
  }

  public void add(Uuid userid, Uuid interest, Time creationTime) {
    Interests interests = interestsByUserId().first(userid);
    if (interests != null) {
      interests.interests.add(interest);
      return;
    }
    HashSet<Uuid> set = new HashSet<Uuid>();
    set.add(interest);
    interestsByUserId.insert(userid, new Interests(set, userid, creationTime, creationTime));
  }

  public void remove(Uuid userid, Uuid interest) {
    Interests interests = interestsByUserId().first(userid);
    if (interests != null) {
      interests.interests.remove(interest);
      return;
    }
  }

  public StoreAccessor<Uuid, Interests> interestsByUserId() {
    return interestsByUserId;
  }

  public void add(Uuid conversationid, UserAccessLevel access) {
    Set<UserAccessLevel> accesses = accessLevelsByConvId().first(conversationid);
    if (accesses != null) {
      for (UserAccessLevel useraccess : accesses) {
         if (useraccess.getUser() == access.getUser()) {
            useraccess.setAccessLevel(access.getAccessLevel());
            return;
         }
      }
      accesses.add(access);
      return;
    }

    Set<UserAccessLevel> set = new HashSet<UserAccessLevel>();
    set.add(access);
    accessLevelsByConvId.insert(conversationid, set);
  }

  public StoreAccessor<Uuid, Set<UserAccessLevel>> accessLevelsByConvId() {
    return accessLevelsByConvId;
  }
}

@@ -5429,20 +5429,24 @@
 etUser()
- == 
+.equals(
 access.g
@@ -5454,16 +5454,17 @@
 tUser())
+)
  %7B%0A     

.equals() compare