/*
 * 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.
 */


#ifndef NET_INSTAWEB_REWRITER_PUBLIC_CSS_MINIFY_H_
#define NET_INSTAWEB_REWRITER_PUBLIC_CSS_MINIFY_H_

#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/string_util.h"

namespace Css {
class Stylesheet;
class Charsets;
class Import;
class MediaQuery;
class MediaQueries;
class MediaExpression;
class FontFace;
class Ruleset;
class Selector;
class SimpleSelector;
class SimpleSelectors;
class Declaration;
class Declarations;
class Value;
class Values;
class FunctionParameters;
class UnparsedRegion;
}  // namespace Css

class UnicodeText;

namespace net_instaweb {

class MessageHandler;
class Writer;

// TODO(nikhilmadan): Move to pagespeed/kernel/css/.
class CssMinify {
 public:
  CssMinify(Writer* writer, MessageHandler* handler);
  ~CssMinify();

  // Parses a CSS stylesheet, writing the minified result to the
  // writer supplied to the consructor.  Optionally, parsed URLs can
  // be added to the string-vector passed to set_url_collector.
  bool ParseStylesheet(StringPiece stylesheet_text);

  // Writes minified Stylesheet from already-parsed stylesheet object.
  static bool Stylesheet(const Css::Stylesheet& stylesheet,
                         Writer* writer,
                         MessageHandler* handler);

  // Writes minified Declarations (style attribute contents).
  static bool Declarations(const Css::Declarations& declarations,
                           Writer* writer,
                           MessageHandler* handler);

  // Establishes a string-vector to collect all parsed URLs.
  void set_url_collector(StringVector* urls) { url_collector_ = urls; }

  // Sets a writer to receive a stream of error messages.  The default is
  // that all error messages are eaten.
  void set_error_writer(Writer* writer) { error_writer_ = writer; }

 private:
  void Write(const StringPiece& str);
  void WriteURL(const UnicodeText& url);

  template<typename Container>
  void JoinMinify(const Container& container, const StringPiece& sep);
  template<typename Iterator>
  void JoinMinifyIter(const Iterator& begin, const Iterator& end,
                      const StringPiece& sep);

  // We name all of these methods identically to simplify the writing of the
  // templated Join* methods.
  void Minify(const Css::Stylesheet& stylesheet);
  void Minify(const Css::Charsets& charsets);
  void Minify(const Css::Import& import);
  void Minify(const Css::MediaQuery& media_query);
  void Minify(const Css::MediaExpression& expression);
  void Minify(const Css::Selector& selector);
  void Minify(const Css::SimpleSelectors& sselectors, bool isfirst = false);
  void Minify(const Css::SimpleSelector& sselector);
  void Minify(const Css::Declaration& declaration);
  void Minify(const Css::Value& value);
  void Minify(const Css::FunctionParameters& parameters);
  void Minify(const Css::UnparsedRegion& unparsed_region);

  // Specializations for Ruleset to handle common @media rules.
  // Start followed by Ignoring followed by End gives the same result as the
  // Ruleset version of Minify above.

  // Emits the start of the @media rule iff required (non-empty media set).
  void MinifyMediaStart(const Css::MediaQueries& media_queries);
  // Emits the end of the @media rule iff required (non-empty media set).
  void MinifyMediaEnd(const Css::MediaQueries& media_queries);

  // Emits rulesets or @font-faces without @media wrappers.
  void MinifyFontFaceIgnoringMedia(const Css::FontFace& font_face);
  void MinifyRulesetIgnoringMedia(const Css::Ruleset& ruleset);

  // Font requires special output format.
  void MinifyFont(const Css::Values& font_values);

  bool Equals(const Css::MediaQueries& a, const Css::MediaQueries& b) const;
  bool Equals(const Css::MediaQuery& a, const Css::MediaQuery& b) const;
  bool Equals(const Css::MediaExpression& a,
              const Css::MediaExpression& b) const;

  bool UnitsRequiredForValueZero(const GoogleString& unit);

  Writer* writer_;
  Writer* error_writer_;
  MessageHandler* handler_;
  bool ok_;

  StringVector* url_collector_;
  bool in_css_calc_function_;

  DISALLOW_COPY_AND_ASSIGN(CssMinify);
};

}  // namespace net_instaweb

#endif  // NET_INSTAWEB_REWRITER_PUBLIC_CSS_MINIFY_H_
