/*
* Copyright 2012-2015 Institut National de l'Audiovisuel
*
* This file is part of Rex.
*
* Rex is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rex 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Rex. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.ina.research.rex.commons.tc;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import fr.ina.research.amalia.AmaliaException;
/**
*
* @author Nicolas HERVE - nherve@ina.fr
*/
public class RexTimeCode implements Comparable<RexTimeCode> {
public final static double DEFAULT_FPS = 25d;
public final static String DEFAULT_PATTERN = "([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{4})";
public final static String DEFAULT_FORMAT = "%02d:%02d:%02d.%04d";
public final static double MS = 10000d;
private static Pattern pattern;
public static RexTimeCode build(int h, int m, int s, int ms) throws AmaliaException {
if (h < 0) {
throw new AmaliaException("h must be >= 0");
}
if (m < 0) {
throw new AmaliaException("m must be >= 0");
}
if (s < 0) {
throw new AmaliaException("s must be >= 0");
}
if (ms < 0) {
throw new AmaliaException("ms must be >= 0");
}
return new RexTimeCode((h * 3600d) + (m * 60d) + s + (ms / MS));
}
public static synchronized void initPattern() throws AmaliaException {
if (pattern == null) {
initPattern(DEFAULT_PATTERN);
}
}
public static synchronized void initPattern(String str) throws AmaliaException {
try {
pattern = Pattern.compile(str);
} catch (PatternSyntaxException e) {
throw new AmaliaException(e);
}
}
public static RexTimeCode parse(String tc) throws AmaliaException {
initPattern();
Matcher m = pattern.matcher(tc);
if (!m.matches()) {
throw new AmaliaException("Invalid pattern for RexTimeCode : " + tc);
}
if (m.groupCount() != 4) {
throw new AmaliaException("Invalid pattern for RexTimeCode (" + m.groupCount() + ")");
}
try {
return build(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)));
} catch (NumberFormatException e) {
throw new AmaliaException(e);
}
}
public static boolean validate(String tc) throws AmaliaException {
initPattern();
return pattern.matcher(tc).matches();
}
private double second;
public RexTimeCode(double second) {
super();
this.second = second;
}
public RexTimeCode(long frame) {
this(frame, DEFAULT_FPS);
}
public RexTimeCode(long frame, double fps) {
super();
setFrame(frame, fps);
}
public void add(double s) throws AmaliaException {
if (s < 0) {
throw new AmaliaException("Use remove() for negative values");
}
second += s;
}
public void add(long frame, double fps) throws AmaliaException {
add(frame / fps);
}
public void add(RexTimeCode o) throws AmaliaException {
add(o.getSecond());
}
@Override
public int compareTo(RexTimeCode o) {
return (int) Math.signum(getSecond() - o.getSecond());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RexTimeCode other = (RexTimeCode) obj;
if (Double.doubleToLongBits(second) != Double.doubleToLongBits(other.second)) {
return false;
}
return true;
}
public long getFrame(double fps) {
return Math.round(second * fps);
}
public double getSecond() {
return second;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(second);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
return result;
}
public void remove(double s) throws AmaliaException {
second -= s;
if (second < 0) {
throw new AmaliaException("Negative timecodes not allowed yet");
}
}
public void remove(long frame, double fps) throws AmaliaException {
remove(frame / fps);
}
public void remove(RexTimeCode o) throws AmaliaException {
remove(o.getSecond());
}
public void setFrame(long frame, double fps) {
setSecond(frame / fps);
}
public void setSecond(double second) {
this.second = second;
}
@Override
public String toString() {
return toString(DEFAULT_FORMAT);
}
public String toString(String format) {
int h = (int) (second / 3600);
double left = second - (h * 3600);
int m = (int) (left / 60);
left = left - (m * 60);
int s = (int) Math.floor(left);
left = left - s;
int ms = (int) Math.round(left * MS);
return String.format(format, h, m, s, ms);
}
}