// Copyright (c) 2014 Baidu, Inc.
// 
// Licensed 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.

// Author: Ge,Jun (gejun@baidu.com)
// Date: 2014/09/22 11:57:43

#ifndef  BVAR_VARIABLE_H
#define  BVAR_VARIABLE_H

#include <ostream>                     // std::ostream
#include <string>                      // std::string
#include <vector>                      // std::vector
#include <gflags/gflags_declare.h>
#include "butil/macros.h"               // DISALLOW_COPY_AND_ASSIGN
#include "butil/strings/string_piece.h" // butil::StringPiece

#ifdef BAIDU_INTERNAL
#include <boost/any.hpp>
#else
namespace boost {
class any;
}
#endif

namespace bvar {

DECLARE_bool(save_series);

// Bitwise masks of displayable targets 
enum DisplayFilter {
    DISPLAY_ON_HTML = 1,
    DISPLAY_ON_PLAIN_TEXT = 2,
    DISPLAY_ON_ALL = 3,
};

// Implement this class to write variables into different places.
// If dump() returns false, Variable::dump_exposed() stops and returns -1.
class Dumper {
public:
    virtual ~Dumper() { }
    virtual bool dump(const std::string& name,
                      const butil::StringPiece& description) = 0;
};

// Options for Variable::dump_exposed().
struct DumpOptions {
    // Constructed with default options.
    DumpOptions();

    // If this is true, string-type values will be quoted.
    bool quote_string;

    // The ? in wildcards. Wildcards in URL need to use another character
    // because ? is reserved.
    char question_mark;

    // Dump variables with matched display_filter
    DisplayFilter display_filter;

    // Name matched by these wildcards (or exact names) are kept.
    std::string white_wildcards;

    // Name matched by these wildcards (or exact names) are skipped.
    std::string black_wildcards;
};

struct SeriesOptions {
    SeriesOptions() : fixed_length(true), test_only(false) {}
    
    bool fixed_length; // useless now
    bool test_only;
};

// Base class of all bvar.
//
// About thread-safety:
//   bvar is thread-compatible:
//     Namely you can create/destroy/expose/hide or do whatever you want to
//     different bvar simultaneously in different threads.
//   bvar is NOT thread-safe:
//     You should not operate one bvar from different threads simultaneously.
//     If you need to, protect the ops with locks. Similarly with ordinary
//     variables, const methods are thread-safe, namely you can call
//     describe()/get_description()/get_value() etc from diferent threads
//     safely (provided that there's no non-const methods going on).
class Variable {
public:
    Variable() {}
    virtual ~Variable();

    // Implement this method to print the variable into ostream.
    virtual void describe(std::ostream&, bool quote_string) const = 0;

    // string form of describe().
    std::string get_description() const;

#ifdef BAIDU_INTERNAL
    // Get value.
    // If subclass does not override this method, the value is the description
    // and the type is std::string.
    virtual void get_value(boost::any* value) const;
#endif

    // Describe saved series as a json-string into the stream.
    // The output will be ploted by flot.js
    // Returns 0 on success, 1 otherwise(this variable does not save series).
    virtual int describe_series(std::ostream&, const SeriesOptions&) const
    { return 1; }

    // Expose this variable globally so that it's counted in following
    // functions:
    //   list_exposed
    //   count_exposed
    //   describe_exposed
    //   find_exposed
    // Return 0 on success, -1 otherwise.
    int expose(const butil::StringPiece& name,
               DisplayFilter display_filter = DISPLAY_ON_ALL) {
        return expose_impl(butil::StringPiece(), name, display_filter);
    }
 
    // Expose this variable with a prefix.
    // Example:
    //   namespace foo {
    //   namespace bar {
    //   class ApplePie {
    //       ApplePie() {
    //           // foo_bar_apple_pie_error
    //           _error.expose_as("foo_bar_apple_pie", "error");
    //       }
    //   private:
    //       bvar::Adder<int> _error;
    //   };
    //   }  // foo
    //   }  // bar
    // Returns 0 on success, -1 otherwise.
    int expose_as(const butil::StringPiece& prefix,
                  const butil::StringPiece& name,
                  DisplayFilter display_filter = DISPLAY_ON_ALL) {
        return expose_impl(prefix, name, display_filter);
    }

    // Hide this variable so that it's not counted in *_exposed functions.
    // Returns false if this variable is already hidden.
    // CAUTION!! Subclasses must call hide() manually to avoid displaying
    // a variable that is just destructing.
    bool hide();

    // Get exposed name. If this variable is not exposed, the name is empty.
    const std::string& name() const { return _name; }

    // ====================================================================
    
    // Put names of all exposed variables into `names'.
    // If you want to print all variables, you have to go through `names'
    // and call `describe_exposed' on each name. This prevents an iteration
    // from taking the lock too long.
    static void list_exposed(std::vector<std::string>* names,
                             DisplayFilter = DISPLAY_ON_ALL);

    // Get number of exposed variables.
    static size_t count_exposed();

    // Find an exposed variable by `name' and put its description into `os'.
    // Returns 0 on found, -1 otherwise.
    static int describe_exposed(const std::string& name,
                                std::ostream& os,
                                bool quote_string = false,
                                DisplayFilter = DISPLAY_ON_ALL);
    // String form. Returns empty string when not found.
    static std::string describe_exposed(const std::string& name,
                                        bool quote_string = false,
                                        DisplayFilter = DISPLAY_ON_ALL);

    // Describe saved series of variable `name' as a json-string into `os'.
    // The output will be ploted by flot.js
    // Returns 0 on success, 1 when the variable does not save series, -1
    // otherwise (no variable named so).
    static int describe_series_exposed(const std::string& name,
                                       std::ostream&,
                                       const SeriesOptions&);

#ifdef BAIDU_INTERNAL
    // Find an exposed variable by `name' and put its value into `value'.
    // Returns 0 on found, -1 otherwise.
    static int get_exposed(const std::string& name, boost::any* value);
#endif

    // Find all exposed variables matching `white_wildcards' but
    // `black_wildcards' and send them to `dumper'.
    // Use default options when `options' is NULL.
    // Return number of dumped variables, -1 on error.
    static int dump_exposed(Dumper* dumper, const DumpOptions* options);

protected:
    virtual int expose_impl(const butil::StringPiece& prefix,
                            const butil::StringPiece& name,
                            DisplayFilter display_filter);

private:
    std::string _name;

    // bvar uses TLS, thus copying/assignment need to copy TLS stuff as well,
    // which is heavy. We disable copying/assignment now.
    DISALLOW_COPY_AND_ASSIGN(Variable);
};

// Make name only use lowercased alphabets / digits / underscores, and append
// the result to `out'.
// Examples:
//   foo-inl.h       -> foo_inl_h
//   foo::bar::Apple -> foo_bar_apple
//   Car_Rot         -> car_rot
//   FooBar          -> foo_bar
//   RPCTest         -> rpctest
//   HELLO           -> hello
void to_underscored_name(std::string* out, const butil::StringPiece& name);

}  // namespace bvar

// Make variables printable.
namespace std {

inline ostream& operator<<(ostream &os, const ::bvar::Variable &var) {
    var.describe(os, false);
    return os;
}

}  // namespace std

#endif  // BVAR_VARIABLE_H