/**
 * Radius Networks, Inc.
 * http://www.radiusnetworks.com
 * 
 * @author David G. Young
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.radiusnetworks.ibeacon;

import android.util.Log;

/**
 * This class represents a criteria of fields used to match iBeacons.  The strange name
 * comes from the iOS implementation, where the idea of a "Region" is also used for a geofence.
 * The idea is that a grouping of one or more iBeacons are analogous to a geofence region.
 * 
 * The uniqueId field is used to distinguish this Region in the system.  When you set up 
 * monitoring or ranging based on a Region and later want to stop monitoring or ranging, 
 * you must do so by passing a Region object that has the same uniqueId field value.  If it
 * doesn't match, you can't cancel the operation.  There is no other purpose to this field.
 * 
 * The other fields: proximityUuid, major and minor are a three part unique identifier for 
 * a single iBeacon.  When constructing a range, any or all of these fields may be set to null,
 * which indicates that they are a wildcard and will match any value.  Note that this differs
 * from the iOS implementation that does not let you set a wildcard on the proximityUuid field.
 * 
 * @author dyoung
 *
 */
public class Region  {
	private static final String TAG = "Region";
	/**
	 * Part 2 of 3 of an iBeacon identifier.  A 16 bit integer typically used to identify a common grouping of iBeacons.
	 */
	protected Integer major;
	/**
	 * Part 3 of 3 of an iBeacon identifier.  A 16 bit integer typically used to identify an individual iBeacon within a group.
	 */
	protected Integer minor;
	/**
	 * Part 1 of 3 of an iBeacon identifier.  A 26 byte UUID typically used to identify the company that owns a set of iBeacons.
	 */
	protected String proximityUuid;
	/**
	 * A unique identifier used to later cancel Ranging and Monitoring, or change the region being Ranged/Monitored
	 */
	protected String uniqueId;
	/**
	 * Constructs a new Region object to be used for Ranging or Monitoring
	 * @param uniqueId - A unique identifier used to later cancel Ranging and Monitoring, or change the region being Ranged/Monitored
	 * @param proximityUuid
	 * @param major
	 * @param minor
	 */
	public Region(String uniqueId, String proximityUuid, Integer major, Integer minor) {
		this.major = major;
		this.minor = minor;
		this.proximityUuid = normalizeProximityUuid(proximityUuid);
		this.uniqueId = uniqueId;
        if (uniqueId == null) {
            throw new NullPointerException("uniqueId may not be null");
        }
	}
	/**
	 * @see #major
	 * @return major
	 */
	public Integer getMajor() {
		return major;
	}
	/**
	 * @see #minor
	 * @return minor
	 */
	public Integer getMinor() {
		return minor;
	}
	/**
	 * @see #proximityUuid
	 * @return proximityUuid
	 */

	public String getProximityUuid() {
		return proximityUuid;
	}
	/**
	 * @see #uniqueId
	 * @return uniqueId
	 */
	public String getUniqueId() {
		return uniqueId;
	}
	
	/**
	 * Checks to see if an IBeacon object is included in the matching criteria of this Region
	 * @param iBeacon the iBeacon to check to see if it is in the Region
	 * @return true if is covered
	 */
	public boolean matchesIBeacon(IBeacon iBeacon) {
		if (proximityUuid != null && !iBeacon.getProximityUuid().equals(proximityUuid)) {
			if (IBeaconManager.debug) Log.d(TAG, "unmatching proxmityUuids: "+iBeacon.getProximityUuid()+" != "+proximityUuid);
			return false;
		}
		if (major != null && iBeacon.getMajor() != major) {
			if (IBeaconManager.debug) Log.d(TAG, "unmatching major: "+iBeacon.getMajor()+" != "+major);
			return false;
		}
		if (minor != null && iBeacon.getMinor() != minor) {
			if (IBeaconManager.debug) Log.d(TAG, "unmatching minor: "+iBeacon.getMajor()+" != "+minor);
			return false;
		}
		return true;
	}
	
	protected Region(Region otherRegion) {
		major = otherRegion.major;
		minor = otherRegion.minor;
		proximityUuid = otherRegion.proximityUuid;
		uniqueId = otherRegion.uniqueId;
	}
	protected Region() {
		
	}

	@Override
	public int hashCode() {
		// return this.uniqueId.hashCode();
        return (this.uniqueId + this.proximityUuid + this.major + this.minor).hashCode();
	}
	
	public boolean equals(Object other) {
		 if (other instanceof Region) {
             // return ((Region)other).uniqueId.equals(this.uniqueId);
             Region r = (Region) other;
             return r.uniqueId.equals(this.uniqueId) &&
                 r.proximityUuid.equals(this.proximityUuid) &&
                 r.major.equals(this.major) && 
                 r.minor.equals(this.minor);
		 }
		 return false;
	}
	
	public String toString() {
		return "proximityUuid: "+proximityUuid+" major: "+major+" minor:"+minor;
	}
	
	/**
	 * Puts string to a normalized UUID format, or throws a runtime exception if it contains non-hex digits
	 * other than dashes or spaces, or if it doesn't contain exactly 32 hex digits
	 * @param proximityUuid uuid with any combination of upper/lower case hex characters, dashes and spaces
	 * @return a normalized string, all lower case hex characters with dashes in the form e2c56db5-dffb-48d2-b060-d0f5a71096e0
	 */
	public static String normalizeProximityUuid(String proximityUuid) {
		if (proximityUuid == null) {
			return null;			
		}
		String dashlessUuid = proximityUuid.toLowerCase().replaceAll("[\\-\\s]", "");
		if (dashlessUuid.length() != 32) {
			// TODO: make this a specific exception
			throw new RuntimeException("UUID: "+proximityUuid+" is too short.  Must contain exactly 32 hex digits, and there are this value has "+dashlessUuid.length()+" digits.");
		}
		if (!dashlessUuid.matches("^[a-fA-F0-9]*$")) {
			// TODO: make this a specific exception
			throw new RuntimeException("UUID: "+proximityUuid+" contains invalid characters.  Must be dashes, a-f and 0-9 characters only.");			
		}
		StringBuilder sb = new StringBuilder();
		sb.append(dashlessUuid.substring(0,8));
		sb.append('-');
		sb.append(dashlessUuid.substring(8,12));
		sb.append('-');
		sb.append(dashlessUuid.substring(12,16));
		sb.append('-');
		sb.append(dashlessUuid.substring(16,20));
		sb.append('-');
		sb.append(dashlessUuid.substring(20,32));
		return sb.toString();
	}

    @Override
    public Object clone() {
        return new Region(this);
    }

}

@@ -4828,46 +4828,8 @@
 ) %7B%0A
-%09%09// return this.uniqueId.hashCode();%0A
     
@@ -4996,79 +4996,8 @@
 ) %7B%0A
-             // return ((Region)other).uniqueId.equals(this.uniqueId);%0A
     

remove unnecessary code