/*
 * 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 PAGESPEED_KERNEL_BASE_FUNCTION_H_
#define PAGESPEED_KERNEL_BASE_FUNCTION_H_

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

namespace net_instaweb {

// Encapsulates a task to be run in response to some event, such as
// a Timer callback, an fetch, or a cache lookup.
//
// Users of interfaces requiring a Function* can either derive a new
// class from Function, or use one of the helper template-classes or
// MakeFunction variants below, which create delayed calls to class
// methods.
//
// Note that Functions by default are self-deleting after call, but
// you can override that with set_delete_after_callback(false).
//
// A Function will always have its Run method or its Cancel method
// called, never both.  A Function should never be deleted without its
// Run/Cancel method being called (except if
// set_delete_after_callback(false)).
//
// Note that classes calling Functions use the CallRun or CallCancel methods,
// rather than calling Run or Cancel directly.  This allows the Function class
// to enforce policy on making run & cancel mutually exclusive and implement
// delete-after-run.
class Function {
 public:
  Function();
  virtual ~Function();

  // Functions used as Worker tasks can help the system shut down cleanly
  // by calling quit_requested() periodically.  To support this, Worker
  // calls set_quit_requested_pointer so the Function object can access
  // the atomic bool.
  //
  // Note that a strategy of allocating the AtomicBool inside the Function
  // object exposes a shutdown race when the function completes and deletes
  // itself.
  void set_quit_requested_pointer(AtomicBool* x) { quit_requested_ = x; }

  // Allows an infrastructure (e.g. Worker or Alarm) to request that
  // a running Function stop soon, as it is being shut down.
  //
  // This can should only be called during the Run() method.
  bool quit_requested() const {
    return (quit_requested_ != NULL) && quit_requested_->value();
  }

  // Implementors of Function interfaces should call via these helper methods
  // to initiate Run and Cancel callbacks.  This helps centralize deletion of
  // callbacks after they are called.
  void CallRun();
  void CallCancel();

  // By default, Functions delete themselves after being called.  Call
  // this method to override.  If the Function is going to be re-called,
  // Reset() must be called on it first. Will not take effect if called
  // within Run() or Cancel().
  void set_delete_after_callback(bool x) { delete_after_callback_ = x; }

  // Clears the state of the function so that it can be called or cancelled
  // again.  This only makes sense to call if set_delete_after_callback(false)
  // has been called.
  void Reset();

 protected:
  // Callers must override this to define the action to take when a closure
  // is run.  If this is called, Cancel() should not be called.  This is
  // a convention that's expected of callers of Function objects, but is
  // not enforced by the Function implementation.
  virtual void Run() = 0;

  // Informs a the Function that it is being shut down.  If this is
  // called, Run() should not be called.  This should never be called
  // while a function is running.  See also set_quit_requested(),
  // which can be called during Run(), so that Run() implementations
  // can check quit_requested() at their convenience to stop the
  // operation in progress.
  virtual void Cancel() {}

 private:
  AtomicBool* quit_requested_;
  bool run_called_;
  bool cancel_called_;
  bool delete_after_callback_;

  DISALLOW_COPY_AND_ASSIGN(Function);
};

// A Macro is recommended for making a readable call to a pointer-to-member
// function per section 33.6 of
// http://www.parashift.com/c++-faq-lite/pointers-to-members.html
#define CALL_MEMBER_FN(object, ptrToMember) ((this->object)->*(ptrToMember))

// Base class for a MemberFunctionN classes, implementing an optional Cancel
// via a Member function.
template<class C>
class MemberFunctionBase : public Function {
 public:
  explicit MemberFunctionBase(C* c) : object_(c) {}

 protected:
  C* object_;
};

// Captures a delayed call to a 0-arg member function as a closure.
template<class C>
class MemberFunction0 : public MemberFunctionBase<C> {
 public:
  typedef void (C::*Func)();

  // Constructor supplying a Run method, but no Cancel method.
  MemberFunction0(Func f, C* c) : MemberFunctionBase<C>(c), run_(f),
                                  cancel_(NULL) {
  }

  // Constructor supplying a Run method and a Cancel method.
  MemberFunction0(Func f, Func cancel, C* c)
      : MemberFunctionBase<C>(c), run_(f), cancel_(cancel) {}

 protected:
  virtual void Run() { CALL_MEMBER_FN(object_, run_)(); }
  virtual void Cancel() {
    if (cancel_ != NULL) {
      CALL_MEMBER_FN(object_, cancel_)();
    }
  }

 private:
  Func run_;
  Func cancel_;
};

// Captures a delayed call to a 1-arg member function as a closure.
template<class C, typename T1>
class MemberFunction1 : public MemberFunctionBase<C> {
 public:
  typedef void (C::*Func)(T1);

  // Constructor supplying a Run method, but no Cancel method.
  MemberFunction1(Func f, C* c, T1 v1)
      : MemberFunctionBase<C>(c), run_(f), cancel_(NULL), v1_(v1) {}

  // Constructor supplying a Run method and a Cancel method.
  MemberFunction1(Func f, Func cancel,
                  C* c, T1 v1)
      : MemberFunctionBase<C>(c), run_(f), cancel_(cancel), v1_(v1)  {}

 protected:
  virtual void Run() { CALL_MEMBER_FN(object_, run_)(v1_); }
  virtual void Cancel() {
    if (cancel_ != NULL) {
      CALL_MEMBER_FN(object_, cancel_)(v1_);
    }
  }

 private:
  Func run_;
  Func cancel_;
  T1 v1_;
};

// Captures a delayed call to a 2-arg member function as a closure.
template<class C, typename T1, typename T2>
class MemberFunction2 : public MemberFunctionBase<C> {
 public:
  typedef void (C::*Func)(T1, T2);

  // Constructor supplying a Run method, but no Cancel method.
  MemberFunction2(Func f, C* c, T1 v1, T2 v2)
      : MemberFunctionBase<C>(c), run_(f), cancel_(NULL), v1_(v1), v2_(v2) {}

  // Constructor supplying a Run method and a Cancel method.
  MemberFunction2(Func f, Func cancel,
                  C* c, T1 v1, T2 v2)
      : MemberFunctionBase<C>(c),
        run_(f), cancel_(cancel),
        v1_(v1), v2_(v2)  {}

 protected:
  virtual void Run() { CALL_MEMBER_FN(object_, run_)(v1_, v2_); }
  virtual void Cancel() {
    if (cancel_ != NULL) {
      CALL_MEMBER_FN(object_, cancel_)(v1_, v2_);
    }
  }

 private:
  Func run_;
  Func cancel_;
  T1 v1_;
  T2 v2_;
};

// Captures a delayed call to a 3-arg member function as a closure.
template<class C, typename T1, typename T2, typename T3>
class MemberFunction3 : public MemberFunctionBase<C> {
 public:
  typedef void (C::*Func)(T1, T2, T3);

  // Constructor supplying a Run method, but no Cancel method.
  MemberFunction3(Func f, C* c, T1 v1, T2 v2, T3 v3)
      : MemberFunctionBase<C>(c), run_(f), cancel_(NULL), v1_(v1), v2_(v2),
        v3_(v3) {
  }

  // Constructor supplying a Run method and a Cancel method.
  MemberFunction3(Func f, Func cancel,
                  C* c, T1 v1, T2 v2, T3 v3)
      : MemberFunctionBase<C>(c),
        run_(f), cancel_(cancel),
        v1_(v1), v2_(v2), v3_(v3)  {}

 protected:
  virtual void Run() { CALL_MEMBER_FN(object_, run_)(v1_, v2_, v3_); }
  virtual void Cancel() {
    if (cancel_ != NULL) {
      CALL_MEMBER_FN(object_, cancel_)(v1_, v2_, v3_);
    }
  }

 private:
  Func run_;
  Func cancel_;
  T1 v1_;
  T2 v2_;
  T3 v3_;
};

// Captures a delayed call to a 4-arg member function as a closure.
template<class C, typename T1, typename T2, typename T3, typename T4>
class MemberFunction4 : public MemberFunctionBase<C> {
 public:
  typedef void (C::*Func)(T1, T2, T3, T4);

  // Constructor supplying a Run method, but no Cancel method.
  MemberFunction4(Func f, C* c, T1 v1, T2 v2, T3 v3, T4 v4)
      : MemberFunctionBase<C>(c), run_(f), cancel_(NULL), v1_(v1), v2_(v2),
        v3_(v3), v4_(v4) {
  }

  // Constructor supplying a Run method and a Cancel method.
  MemberFunction4(Func f, Func cancel,
                  C* c, T1 v1, T2 v2, T3 v3, T4 v4)
      : MemberFunctionBase<C>(c),
        run_(f), cancel_(cancel),
        v1_(v1), v2_(v2), v3_(v3), v4_(v4)  {}

 protected:
  virtual void Run() { CALL_MEMBER_FN(object_, run_)(v1_, v2_, v3_, v4_); }
  virtual void Cancel() {
    if (cancel_ != NULL) {
      CALL_MEMBER_FN(object_, cancel_)(v1_, v2_, v3_, v4_);
    }
  }

 private:
  Func run_;
  Func cancel_;
  T1 v1_;
  T2 v2_;
  T3 v3_;
  T4 v4_;
};

#undef CALL_MEMBER_FN

// Makes a Function* that calls a 0-arg class method.
template<class C>
Function* MakeFunction(C* object, void (C::*run)()) {
  return new MemberFunction0<C>(run, object);
}

// Makes a Function* that calls a 0-arg class method, or a 0-arg cancel
// method.
template<class C>
Function* MakeFunction(C* object, void (C::*run)(), void (C::*cancel)()) {
  return new MemberFunction0<C>(run, cancel, object);
}

// Makes a Function* that calls a 1-arg class method.
template<class C, class T>
Function* MakeFunction(C* object, void (C::*run)(T), T t) {
  return new MemberFunction1<C, T>(run, object, t);
}

// Makes a Function* that calls a 1-arg class method, or a 1-arg cancel
// method.
template<class C, class T>
Function* MakeFunction(C* object,
                       void (C::*run)(T),
                       void (C::*cancel)(T), T t) {
  return new MemberFunction1<C, T>(run, cancel, object, t);
}

// Makes a Function* that calls a 2-arg class method.
template<class C, class T, class U>
Function* MakeFunction(C* object, void (C::*run)(T, U), T t, U u) {
  return new MemberFunction2<C, T, U>(run, object, t, u);
}

// Makes a Function* that calls a 2-arg class method, or a 2-arg cancel
// method.
template<class C, class T, class U>
Function* MakeFunction(C* object,
                       void (C::*run)(T, U),
                       void (C::*cancel)(T, U),
                       T t, U u) {
  return new MemberFunction2<C, T, U>(run, cancel, object, t, u);
}

// Makes a Function* that calls a 3-arg class method.
template<class C, class T, class U, class V>
Function* MakeFunction(C* object,
                       void (C::*run)(T, U, V),
                       T t, U u, V v) {
  return new MemberFunction3<C, T, U, V>(run, object, t, u, v);
}

// Makes a Function* that calls a 3-arg class method, or a 3-arg cancel
// method.
template<class C, class T, class U, class V>
Function* MakeFunction(C* object,
                       void (C::*run)(T, U, V),
                       void (C::*cancel)(T, U, V),
                       T t, U u, V v) {
  return new MemberFunction3<C, T, U, V>(run, cancel, object, t, u, v);
}

// Makes a Function* that calls a 4-arg class method.
template<class C, class T, class U, class V, class W>
Function* MakeFunction(C* object,
                       void (C::*run)(T, U, V, W),
                       T t, U u, V v, W w) {
  return new MemberFunction4<C, T, U, V, W>(run, object, t, u, v, w);
}

// Makes a Function* that calls a 4-arg class method, or a 4-arg cancel
// method.
template<class C, class T, class U, class V, class W>
Function* MakeFunction(C* object,
                       void (C::*run)(T, U, V, W),
                       void (C::*cancel)(T, U, V, W),
                       T t, U u, V v, W w) {
  return new MemberFunction4<C, T, U, V, W>(run, cancel, object, t, u, v, w);
}

}  // namespace net_instaweb

#endif  // PAGESPEED_KERNEL_BASE_FUNCTION_H_
