import 'package:meta/meta.dart';

@immutable
class JID {

  const JID(this.local, this.domain, this.resource);

  factory JID.fromString(String jid) {
    // 0: Parsing either the local or domain part
    // 1: Parsing the domain part
    // 2: Parsing the resource
    var state = 0;
    var buffer = '';
    var local_ = '';
    var domain_ = '';
    var resource_ = '';
    
    for (var i = 0; i < jid.length; i++) {
      final c = jid[i];
      final eol = i == jid.length - 1;
      
      switch (state) {
        case 0: {
          if (c == '@') {
            local_ = buffer;
            buffer = '';
            state = 1;
          } else if (c == '/') {
            domain_ = buffer;
            buffer = '';
            state = 2;
          } else if (eol) {
            domain_ = buffer + c;
          } else {
            buffer += c;
          }
        }
        break;
        case 1: {
          if (c == '/') {
            domain_ = buffer;
            buffer = '';
            state = 2;
          } else if (eol) {
            domain_ = buffer;

            if (c != ' ') {
              domain_ = domain_ + c;
            }
          } else if (c != ' ') {
            buffer += c;
          }
        }
        break;
        case 2: {
          if (eol) {
            resource_ = buffer;

            if (c != ' ') {
              resource_ = resource_ + c;
            }
          } else if (c != ''){
            buffer += c;
          }
        }
      }
    }

    return JID(local_, domain_, resource_);
  }
  final String local;
  final String domain;
  final String resource;

  bool isBare() => resource.isEmpty;
  bool isFull() => resource.isNotEmpty;

  JID toBare() => JID(local, domain, '');
  JID withResource(String resource) => JID(local, domain, resource);
  
  @override
  String toString() {
    var result = '';

    if (local.isNotEmpty) {
      result += '$local@$domain';
    } else {
      result += domain;
    }
    if (isFull()) {
      result += '/$resource';
    }

    return result;
  }

  @override
  bool operator ==(Object other) {
    if (other is JID) {
      return other.local == local && other.domain == domain && other.resource == resource;
    }

    return false;
  }

  @override
  int get hashCode => local.hashCode ^ domain.hashCode ^ resource.hashCode;
}
