import java.util.*;
import java.util.regex.*;
import java.io.*;
import java.lang.*;

public class CSSMin {

	public static void main(String[] args) {
		if (args.length < 1) {
			System.out.println("You need to supply an input file, and optionally an output file (output is to stdout otherwise).");
			return;
		}
		
		PrintStream out;
		
		if (args.length > 1) {
			try {
				out = new PrintStream(args[1]);
			} catch (Exception e) {
				System.out.println("Error outputting to " + args[1] + "; redirecting to stdout");
				out = System.out;
			}
		} else {
			out = System.out;
		}
		
		formatFile(args[0], out);
	}
	
	public static void formatFile(String f, PrintStream out) {
		try {
			int k, n;
			
			//System.out.println("Reading file contents...");
			
			BufferedReader br = new BufferedReader(new FileReader(f));
			StringBuffer sb = new StringBuffer();
			
			String s;
			while ((s = br.readLine()) != null) {
				if (s.trim().equals("")) continue;
				sb.append(s.replaceAll("[\t\n\r]","").replaceAll("  ", " "));
			}
			//System.out.println("Finished reading file contents.");
			
			
			//System.out.println("Stripping comments...");
			// Find the start of the comment
			while ((n = sb.indexOf("/*")) != -1) {
				k = sb.indexOf("*/", n + 2);
				if (k == -1) {
					throw new Exception("Error: Unterminated comment.");
				}
				sb.delete(n, k + 2);
			}
			//System.out.println("Finished stripping comments.");
			
			
			//System.out.println("Extracting & parsing selectors...");
			Vector<Selector> selectors = new Vector<Selector>();
			n = 0;
			while ((k = sb.indexOf("}", n)) != -1) {
				try {
					selectors.addElement(new Selector(sb.substring(n, k + 1)));
				} catch (Exception e) {
					//System.out.println("  Error parsing selector: skipping...");
				}
				n = k + 1;
			}
			//System.out.println("Finished extracting & parsing selectors.");
			
			
			//System.out.println("Pretty-printing output...");
			
			for (Selector selector : selectors) {
				out.print(selector.toString());
			}
			out.print("\r\n");
			
			out.close();
			
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
		
		
	}
}

class Selector {
	private Property[] properties;
	private String selector;
	
	/**
	 * Creates a new Selector using the supplied strings.
	 * @param selector The selector; for example, "div { border: solid 1px red; color: blue; }"
	 */
	public Selector(String selector) throws Exception {
		String[] parts = selector.split("\\{"); // We have to escape the { with a \ for the regex, which itself requires escaping for the string. Sigh.
		if (parts.length < 2) {
			throw new Exception("Warning: Incomplete selector: " + selector);
		}
		this.selector = parts[0].trim();
		String contents = parts[1].trim();
		if (contents.length() == 0) {
			throw new Exception("Warning: Empty selector body: " + selector);
		}
		if (contents.charAt(contents.length() - 1) != '}') { // Ensure that we have a leading and trailing brace.
			throw new Exception("Warning: Unterminated selector: " + selector);
		}
		contents = contents.substring(0, contents.length() - 2);
		this.properties = parseProperties(contents);
		sortProperties(this.properties);
	}
	
	/**
	 * Prints out this selector and its contents nicely, with the contents sorted alphabetically.
	 */
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append(this.selector).append("{");
		for (Property p : this.properties) {
			sb.append(p.toString());
		}
		sb.append("}");
		return sb.toString();
	}
	
	/**
	 * Parses out the properties of a selector's body.
	 * @param contents The body; for example, "border: solid 1px red; color: blue;"
	 */
	private Property[] parseProperties(String contents) {
		String[] parts = contents.split(";");
		Property[] results = new Property[parts.length];
		
		for (int i = 0; i < parts.length; i++) {
			try {
				results[i] = new Property(parts[i]);
			} catch (Exception e) {
				System.err.println(e.getMessage());
				results[i] = null;
			}
		}
		
		return results;
	}
	
	private void sortProperties(Property[] properties) {
		Arrays.sort(properties);
	}
}

class Property implements Comparable<Property> {
	protected String property;
	protected Value[] values;
	
	/**
	 * Creates a new Property using the supplied strings. Parses out the values of the property selector.
	 * @param property The property; for example, "border: solid 1px red;" or "-moz-box-shadow: 3px 3px 3px rgba(255, 255, 0, 0.5);".
	 */
	public Property(String property) throws Exception {
		try {
			// Parse the property.
			String[] parts = property.split(":"); // Split "color: red" to ["color", " red"]
			if (parts.length < 2) {
				throw new Exception("Warning: Incomplete property: " + property);
			}
			this.property = parts[0].trim().toLowerCase();
			
			this.values = parseValues(parts[1].trim().replaceAll(", ", ","));
			
		} catch (PatternSyntaxException e) {
			// Invalid regular expression used.
		}
	}
	
	/**
	 * Prints out this property nicely, with the contents sorted in a standardised order.
	 */
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append(this.property).append(":");
		for (Value v : this.values) {
			sb.append(v.toString()).append(",");
		}
		sb.deleteCharAt(sb.length() - 1); // Delete the trailing comma.
		sb.append(";");
		return sb.toString();
	}
	
	public int compareTo(Property other) {
		return this.property.compareTo(other.property);
	}
	
	private Value[] parseValues(String contents) {
		String[] parts = contents.split(",");
		Value[] results = new Value[parts.length];
		
		for (int i = 0; i < parts.length; i++) {
			try {
				results[i] = new Value(parts[i]);
			} catch (Exception e) {
				System.err.println(e.getMessage());
				results[i] = null;
			}
		}
		
		return results;
	}
}

class Value {
	String[] parts;
	
	public Value(String value) throws Exception {
		// Parse the value.
		this.parts = value.split(" "); // Split "solid 1px red" to ["solid","1px","red"] and sort them.
	}
	
	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (String s : this.parts) {
			sb.append(s).append(" ");
		}
		return sb.toString().trim();
	}
}